Case Study · ENSF 380
Disaster Relief System
A Java desktop application for coordinating disaster response. Relief workers can track victims, manage shelter supplies, log missing-person inquiries, and record medical history — all backed by a live PostgreSQL database and tested with 31 JUnit tests.

Overview
What it does
Victim Management
Add, update, and track disaster victims with full medical, cultural, and skills records. Victims can be soft-deleted to a recoverable trash bin — nothing is lost permanently unless you choose.
Supply Tracking
Each location holds its own inventory. On startup the system automatically flags any expired supplies before the user does anything else — proactive, not reactive.
Inquiries & Relations
When someone is reported missing, an inquiry links the reporter to the missing person. Family relationships and medical records are tracked separately, so every connection is auditable.
Built with production-grade architecture
The system uses a five-layer design: GUI → Controller → Service → Repository → Domain. Each layer depends only on the one below it. The practical payoff: swapping PostgreSQL for an in-memory mock (for tests) requires changing exactly one line — the injection point.
Under the Hood
Four decisions worth explaining
Singleton database connection
Only one JDBC connection is ever created. It's initialized when the class loads and shared across every repository. Credentials live in a local file — so no secrets end up in version control.
Repository pattern with mock injection
Every database concern sits behind an interface. Tests inject an in-memory mock; production injects PostgreSQL. The service layer never knows which one it's talking to — which is exactly the point.
Shared InventoryHolder interface
Both victims and locations need to manage supplies. Rather than duplicating that logic, both classes implement a single InventoryHolder interface. One method handles both — no type-checking, no branching.
Soft delete with restore
Removing a victim doesn't drop the record — it moves to a hidden bin. Relief workers can restore anyone or permanently delete when they're sure. This mirrors how real case management tools work: deletions should be undoable.
The Singleton in practice
The pattern is simple but deliberate — eager initialization means the connection is ready before anything else runs, and the private constructor guarantees no second instance can ever be created.
public class DatabaseConnection {
// Eager init — connection is created once when the class loads
private static DatabaseConnection instance = new DatabaseConnection();
private Connection connection;
private DatabaseConnection() {
List<String> credentials = Files.readAllLines(CREDENTIALS_PATH);
this.connection = DriverManager.getConnection(
"jdbc:postgresql://localhost/ensf380project",
credentials.get(0).trim(), // username
credentials.get(1).trim() // password
);
}
// Every repository calls this — they all get the same connection
public static DatabaseConnection getInstance() {
return instance;
}
}Walkthrough
The app in action

The welcome screen is clean on purpose. Relief workers are under stress — the UI doesn't add to it.

Eight modules in the menu bar — every major operation is one click away from anywhere in the app.

Before anything else loads, the system queries for expired supplies and surfaces them in a warning. In a real relief scenario, expired food or medicine is a safety issue — catching it automatically is more reliable than hoping someone remembers to check.

List view on the left, full detail pane on the right. Every field — medical records, supplies, skills — is accessible without leaving this screen.

The Add Victim dialog captures cultural requirements (dietary restrictions, language support, mobility needs) alongside the standard fields — reflecting the real complexity of disaster relief work.
Testing
31 JUnit tests, zero guesswork
Every class in the system has a corresponding test. They all run through a single AllTests suite runner — one command confirms the whole thing works. Mock repositories replace the real database in tests, so they run fast and never depend on a live connection.
Takeaways
What this taught me
Architecture is a communication tool
Layered design isn't just for scalability — it makes the codebase readable to anyone. When a new developer opens the project, the layers tell the story of how data flows.
Testability is a design decision
The repository pattern was chosen because it makes testing clean, not because it was required. Every architectural decision had a reason, and that reason was usually 'so we can test it properly.'
Domain complexity is real
Modelling cultural requirements, approximate ages, soft deletes, and audit logs taught me that real-world domains are messier than toy examples — and that's what makes engineering interesting.