Concurrency-safe status updates, and going live
Dakiya works and is secured and tested. The last step is what separates "works in a demo" from "runs a courier company": making concurrent status updates safe, and taking the whole thing through the production checklist to a live, HTTPS deployment. This closes the capstone — and the course.
Harden the concurrent status updates
A courier API's defining concurrency problem: many riders and hubs update the same parcel's status at
once. Two updates that overlap — both read the parcel, both change it, both save — and the second
silently overwrites the first: the lost update (the concurrency module). Dakiya prevents it with the
@Version field designed into the Parcel entity:
@Entity
public class Parcel {
// ...
@Version
private Long version; // Hibernate checks it on every update
}
Now a status update generates UPDATE parcel SET status = ?, version = 2 WHERE id = ? AND version = 1. If
another update already moved the version to 2, this update matches zero rows and Hibernate throws an
optimistic-lock exception — verified in the concurrency module: version 0 → 1 after the first update, the
second (stale) update rejected, not lost. Dakiya handles the conflict by retrying (reload the fresh
parcel, re-apply the transition, save) or returning 409 Conflict so the client retries:
@Transactional
public Parcel updateStatus(Long id, Status target) {
try {
Parcel p = repository.findById(id).orElseThrow(() -> new NotFoundException("..."));
p.transitionTo(target);
return p;
} catch (OptimisticLockingFailureException e) {
throw new ConflictException("Parcel was updated concurrently; retry"); // → 409
}
}
The lost update becomes a detected, handled conflict — never silent corruption. Money/critical writes are
@Transactional (all-or-nothing), and the unique trackingCode constraint prevents duplicate-booking
races. That is the concurrency toolkit applied to the real system: @Version for status updates,
transactions for grouped writes, a unique constraint for the insert race.
Prepare for production
The production configuration (the production module), for Dakiya:
- Database — real PostgreSQL via
DATABASE_URL;ddl-auto=validate; Flyway owns the schema (aV1__create_schema.sqlmigration creating the parcel/hub/rider/partner tables and constraints). - Secrets — the JWT signing key, DB password from environment variables / a secrets manager, none committed.
- Profiles —
SPRING_PROFILES_ACTIVE=prodloads production settings (INFO logging, no stack traces to clients). - Observability —
/actuator/healthexposed for the platform's health check (verified:{"status":"UP"}), metrics secured, logs to stdout with error tracking. - Container — build an optimised image (
./mvnw spring-boot:build-image), config fed as environment variables at run time.
The launch checklist
Before Dakiya goes live — the whole course, condensed:
- Layering — thin controllers, logic in services, queries in repositories.
- API contract — DTOs (not entities), validation,
ProblemDetailerrors, correct statuses, paginated lists. - Data — no N+1 on hot paths (fetch joins), indexes on filtered columns,
@Enumerated(STRING). - Security — authentication for all clients, role rules, object-level access (partners see only their own), fail-closed default, BCrypt, no stack traces to clients.
- Concurrency —
@Versionon parcels (status-update race handled), transactions on grouped writes, unique constraint on tracking code. - Tests — risky paths (breach, roles, validation, transitions, concurrency) pass in the build.
- Production —
prodprofile, PostgreSQL + Flyway (validate), secrets externalised, tested backups, HTTPS, health check wired, observability configured. - Verify live — call the deployed API over HTTPS: book a parcel, update its status as staff, track
it as the partner, hit
/actuator/health— confirm it works, do not assume.
Work down the checklist and Dakiya goes live as a real, secure, concurrency-safe, tested courier API. That last item is the habit the whole course insists on: a deploy succeeding is not the same as the service working — call the live endpoints and confirm.
The capstone, and the course, complete
You have built a real Spring Boot service end to end: a clean-layered REST API with DTOs, validation and consistent errors; a JPA data layer with sensible relationships, no N+1, and queries that fetch only what they need; authentication, role-based and object-level authorisation protecting sensitive shipment data; concurrency-safe status updates with optimistic locking; a test suite covering the risky paths; and a hardened, HTTPS, PostgreSQL deployment with health checks. Every module of the course is in it — the container and DI, REST, JPA in depth, security, concurrency, testing, architecture, deployment — which is the point of a capstone, and the answer to the question the course kept asking: could you be handed real Spring work and do it, including the hard parts — the concurrency, the security, the performance? Dakiya is the evidence you can. And you can explain what Spring is doing underneath at every step — which is the difference between someone who adds annotations and hopes, and someone worth hiring.
Check your work
Concurrency hardening. @Version on Parcel turns concurrent status updates from a silent lost update
into a detected optimistic-lock conflict (verified: stale update rejected), handled by retry or 409;
transactions for grouped writes; unique trackingCode constraint for the insert race.
Production prep. Real PostgreSQL + Flyway (validate), secrets from the environment, prod profile
(INFO, no stack traces), /actuator/health for the platform (verified UP), an optimised container image,
config as run-time env vars.
Launch checklist. Layering; API contract (DTOs/validation/errors/pagination); data (no N+1, indexes,
STRING enum); security (auth, roles, object-level, fail-closed, BCrypt); concurrency (@Version,
transactions, unique constraint); tests green; production (prod profile, Postgres+Flyway, secrets, tested
backups, HTTPS, health/observability); and verify live, not assume.
The whole. A real courier API exercising every module — the evidence you can do real Spring work, including the hard parts, and explain the framework underneath.
Practice
- Confirm
@VersiononParcel; simulate two concurrent status updates and confirm the stale one is rejected (reproduce the optimistic-lock behaviour); handle it with a retry and with a 409. - Confirm the unique
trackingCodeconstraint prevents two concurrent bookings of the same code. - Write a Flyway
V1__migration for the schema, setddl-auto=validate, and confirm the app boots against it. - Expose
/actuator/health, wire it as the platform's health check, and confirm{"status":"UP"}. - Build a container image, run it with production env vars (profile,
DATABASE_URL, secrets), against a real Postgres. - Run the full launch checklist against a deployed Dakiya, finishing by calling the live API over HTTPS (book, update status, track, health) — verifying it works.
Official documentation
- Spring Data JPA — Locking (@Version) — Optimistic locking for concurrent updates.
- Spring Boot — Deployment and Actuator — Going to production.
- Spring Boot — Reference documentation — The complete reference for everything Dakiya uses.
That is the Java + Spring Web Development course. You built Dakiya — a real courier and parcel-tracking API — from first bean to live deployment, with the concurrency, security and performance done properly, and you can explain what Spring is doing underneath. That is what it takes to be handed real Spring work and do it well.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship