1. Project Planning & Scoping
Before writing a single @RestController, write down what this service
actually does, for whom, and what "working" concretely means. Pick a real,
well-scoped backend problem — not a toy CRUD demo, but not an unbounded platform
either:
- A booking system — appointments or reservations against limited capacity, where double-booking is a real bug to prevent, not a hypothetical.
- An inventory/order service — stock levels that must stay consistent under concurrent orders, with a clear order lifecycle.
- A small SaaS billing service — plans, usage, and invoices, where correctness (nobody gets billed twice) matters more than feature count.
Whichever domain you pick, define the core domain model and API surface before writing code: what are the two or three central entities, what are the main read and write operations against them, and which of those operations are the ones a reviewer would actually exercise to judge whether the service works. Sketch the key endpoints as a short list — resource, verb, and what changes — the same way you'd plan a REST resource model in Week 3, before any of it exists.
Finally, write success criteria down now, not after the fact: a specific, short list of flows that must work end to end (create a booking, prevent a conflicting one, cancel it, see it reflected in availability) decided before you build, so "done" isn't retroactively defined by whatever happened to get built.
package com.codeverse.capstone.booking;
// Written down before any of it exists -- the resource, the verb, what
// changes. Fill this in for your own domain before opening an IDE.
// POST /api/bookings create a booking (or 409 on conflict)
// GET /api/bookings/{id} fetch one booking
// DELETE /api/bookings/{id} cancel a booking
// GET /api/availability?date=... read-heavy -- a caching candidate (Week 11)
public record CreateBookingRequest(Long resourceId, Instant start, Instant end) {}
public record BookingView(Long id, Long resourceId, Instant start, Instant end, String status) {}
2. Architecture Decisions & Tradeoffs
With the service scoped, make deliberate choices using the tradeoffs this course already gave you a framework for — and be ready to justify each one in writing, not just make it:
- Monolith vs. a couple of services (Weeks 9–10) — a single well-structured Spring Boot application is the right default for a capstone-sized domain; only split out a second service if there's a genuinely separate responsibility (e.g. a notification worker consuming events) that benefits from failing and scaling independently.
- Sync vs. async communication — if there is any inter-service call at all, decide per interaction: a synchronous REST call (Week 9's
RestClientplus resilience patterns) for anything the caller needs an immediate answer to, an event published to a broker (Week 10) for anything that can happen after the response has already gone out. - What needs caching (Week 11) vs. what doesn't — cache read-heavy, slow-changing data (a catalog, availability lookups); never cache anything where staleness would let two people book the same slot or double-spend the same balance.
The justification is the actual work here. "I kept this as one service because the domain is small and a network hop between two services would only add latency and failure modes for no real benefit" is an architectural decision; using every technique from the course by default — a message broker for a single-writer counter, caching on a field that changes every request — is not. Write the reasoning down alongside the choice; Section 5's write-up depends on it.
3. Full Implementation, in a Sensible Order
Build in an order that lets you validate each layer before the next depends on it — the same incremental discipline this course used from Week 5 onward, now spanning the whole service:
// 1. Entities and persistence first (Weeks 4-5) -- get the domain model
// into a real database with migrations, before any HTTP layer exists.
// 2. REST layer (Week 3) -- controllers, DTOs, and validation over that
// persisted model; confirm every core flow works unauthenticated.
// 3. Security (Weeks 6-7) -- add authentication and authorization once
// the unsecured flows are already correct, not before.
// 4. Service-to-service or messaging (Weeks 9-10) -- ONLY if Section 2's
// architecture actually calls for it.
// 5. Caching (Week 11) -- applied last, to endpoints Section 2 identified
// as read-heavy, once their uncached behavior is already correct.
This order matters for the same reason it mattered in Week 5's and Week 9's exercises: it's far cheaper to discover your entity relationships or API shape need to change while only the persistence and REST layers exist, than after security, messaging, and caching are all built on top of an assumption that turns out to be wrong. Validate each layer — a passing request against it, by hand or by test — before adding the next.
4. Testing, Observability & Performance Pass
Apply Weeks 8 and 12 in full, not as an afterthought bolted on at the end:
- A full Week 8-style test suite — unit tests for anything with real logic,
@WebMvcTest/@DataJpaTestslice tests for controllers and repositories in isolation, and Testcontainers integration tests that exercise the full stack against a real database (and a real broker, if Section 2 added one). - Week 12 observability — Spring Boot Actuator health checks wired to real dependency checks (not just "the app is up"), meaningful custom metrics beyond the defaults, structured JSON logs, and distributed tracing if the architecture genuinely has more than one service to trace across.
- A basic performance sanity check — connection pool sizing that's been deliberately set rather than left at a default nobody looked at, and an N+1 query check (Week 5's and Week 11's lesson) on every endpoint that returns a collection with associations.
None of this is optional polish. A service with no Testcontainers coverage hasn't actually proven it talks to a real database correctly, and a service with no health checks or metrics can't prove it's healthy in production even if it happens to be — both are exactly the gaps this pass exists to close.
5. Deployment & Portfolio Write-Up
A correct service nobody else can reach is far less useful as a portfolio piece than
a modest one that's actually live. Containerize and deploy it per Week 13 — Cloud
Native Buildpacks or a hand-written Dockerfile, a CI/CD pipeline that
builds and pushes the image on every push, and a real deployment target: Kubernetes
if the architecture warrants it, or a simpler managed platform if it doesn't.
- An architecture diagram or description — what the service (or services) look like, from Section 2's decisions.
- The tradeoffs made, and why — the written justifications from Section 2, not just the final list of technologies used.
- What you'd do differently — an honest note on what you'd change with more time, which reads as more credible than pretending the first version is the final one.
- A reachable endpoint — the deployed service itself, since the write-up is what a reader actually evaluates the project by, not the source code they'd otherwise have to dig through.
6. The Capstone Project
Design, build, test, secure and deploy a complete Spring Boot service
Pick a real, well-scoped backend problem — a booking system, an inventory/order service, a small SaaS billing service — and take it fully through Sections 1–5: planned, architected, implemented, tested and observable, and deployed.
Requirements:
- Write the project scope and API surface (Section 1) before writing any code: core domain model, key endpoints, and success criteria.
- Justify your architecture in writing (Section 2): whether you split services or kept one, sync vs. async for any inter-service calls, and what you chose to cache.
- Implement incrementally through Section 3's layers — persistence, REST, security, messaging if applicable, caching — validating each before adding the next.
- Complete the full testing, observability and performance pass from Section 4, including Testcontainers integration tests and real health checks and metrics.
- Deploy it for real — containerized, ideally to Kubernetes or a managed platform — with a live, reachable endpoint and a portfolio write-up (Section 5).
Not scope or technique count — a small service that's fully working, well-tested, and clearly explained reads as far more credible to anyone reviewing it (an employer, a collaborator, future-you) than an ambitious service that uses every pattern from the course but is only half-finished and untested.
7. Final Checklist
Before calling the capstone — and the course — done, confirm each of these honestly:
✓
Was the scope and API surface written down before any code was written?
Was the scope and API surface written down before any code was written?
If the domain model and endpoint list were written after the service already existed, they describe what got built rather than what was planned — the same discipline behind writing a test's expectations before, not after, checking what the code currently does. A capstone with a scope document written first has a real definition of "done" to be judged against.
✓
Can every major architectural choice — services split or not, sync vs. async, what's cached — be justified, rather than just having used every technique from the course?
Can every major architectural choice — services split or not, sync vs. async, what's cached — be justified, rather than just having used every technique from the course?
Every service split, message broker, and cache layer is added complexity and failure surface to maintain. A capstone that can justify each choice crisply — one service because the domain didn't need more, a cache only on the read-heavy catalog lookup, nothing async because nothing needed to be — reads as far stronger engineering judgment than one that reached for everything the course covered regardless of fit.
✓
Does the full test suite pass, including Testcontainers integration tests against a real database?
Does the full test suite pass, including Testcontainers integration tests against a real database?
Unit and slice tests alone can pass against mocks while the real integration between your entities, queries, and an actual database is still broken. Testcontainers tests are the ones that prove the service works against something close to what it'll run against in production — without them, "the tests pass" is a weaker claim than it sounds like.
✓
Is the service actually deployed and reachable, with a write-up someone could evaluate without reading the source?
Is the service actually deployed and reachable, with a write-up someone could evaluate without reading the source?
A service that only runs on your own machine, with your own local database and config, isn't actually finished — deployment (Section 5) isn't optional polish tacked on at the end; it's the proof the service is real. Paired with a write-up covering the architecture, the tradeoffs, and what you'd change, a live endpoint is the actual deliverable a reader will judge the whole project by.
That's the full 15-week Java & Spring Boot path — from a first
@RestController in Week 1 to a tested, secured, observable, deployed
production service. Congratulations on reaching the capstone.