← Back to Projects

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.

Java 21PostgreSQLSwing GUIJUnit 4JDBCOOP
31JUnit test classes
8management modules
5OOP design patterns
View on GitHub ↗📄 UML diagram in repo

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.

DatabaseConnection.java
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

Welcome screen
01

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

Navigation menu
02

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

Expired supplies warning
03

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.

Victim Management
04

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

Add Victim dialog
05

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.

DisasterVictimTest
LocationTest
ReliefServiceTest
FamilyRelationTest
SupplyTest
MedicalRecordTest
InquirerTest
PersonTest
SkillTest
GenderTest
RelationTest
SkillProficiencyTest
DisasterVictimServiceTest
LocationServiceTest
InquiryServiceTest
PersonServiceTest
RelationshipServiceTest
SupplyManagerTest
OccupantManagerTest
RelationshipManagerTest
SkillManagerTest
LocationInventoryTest
PersonalInventoryTest
MedicalHistoryTest
DatabaseLoaderTest
ActionLoggerTest
CulturalRequirementTest
CulturalOptionsTest
CulturalOptionsLoaderTest
DisasterReliefSystemTest
SupplyTypeTest

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.