Case study 02 · EAV Labs
EAV Dispatch
A Spring Boot logistics API for shipments, drivers, vehicles, assignments, controlled delivery lifecycles and auditable dispatch workflows.

Rules that live in the system, not in memory
I wanted a backend where the hard rules of dispatch live in code and in the database rather than in people’s heads: who is assigned to what, what state a shipment is in, and what happened along the way.
Double-booking and lost history
Dispatch teams juggle drivers, vehicles and shipments under time pressure. When assignments live in chats and spreadsheets, a driver gets booked twice, statuses drift, and nobody can say exactly what happened to a delivery.
What I did
I designed the domain model and lifecycle rules, built the API with Spring Boot, owned the schema through Flyway migrations, and wrote concurrency tests against real PostgreSQL with Testcontainers. Authentication and multi-tenancy are deliberately left for after the MVP.
One deployable, clear boundaries
A modular monolith: shipment workflow, drivers, vehicles, persistence and HTTP concerns sit in explicit package boundaries while staying one deployable application. PostgreSQL is the source of truth.
Decisions that shaped it
No double-booking, enforced in the database
Assignment checks use database locking plus active-assignment queries, verified with concurrent PostgreSQL Testcontainers tests.
Why: Rules enforced only in application code can be raced; database locking makes the guarantee hold under concurrent requests. The trade-off is more careful transactions, and tests that exercise real concurrency.
A controlled shipment lifecycle
Shipments move ASSIGNED → IN_TRANSIT → DELIVERED; terminal states reject further mutation and hard deletion.
Why: A small, explicit set of states makes every shipment’s status unambiguous and keeps finished deliveries trustworthy. The trade-off is that corrections need their own deliberate flows rather than edits.
Schema owned by migrations
Flyway owns schema changes (V1–V4) and Hibernate only validates mappings.
Why: Migrations make every schema change reviewable and repeatable in each environment. Letting Hibernate only validate means the code can never quietly change the database.
Auditable by default
Every lifecycle change is recorded in a chronological audit history with retention safeguards.
Why: When something goes wrong, the history answers who changed what, and when. The cost is more data to keep, which the retention safeguards hold in check.
Running, verified and released
Deployed from main to an OCI ARM64 VM after CI succeeds, with readiness verified locally and publicly. CI covers Maven verification, Testcontainers, a Compose smoke test and CodeQL; releases publish semantic-versioned images.


Known limits
- Authentication and role-based authorisation are post-MVP.
- Multi-tenant organisation boundaries are post-MVP.