Skip to content

Where to go next

Every simplification Recap → named is a door to walk through next. Each of these extends the system you already have without rewriting it — the boundaries you built are the seams you extend along.

  • Authentication & authorization at the gateway. Put identity in front of everything: validate a JWT (or an OIDC access token) in a gateway middleware, reject unauthenticated requests before they reach a gRPC call, and forward the verified identity as gRPC metadata. This teaches where a cross-cutting concern belongs in a system with one front door — at the gateway →, not duplicated in every service — and how to propagate identity across a trust boundary.

  • Observability across every hop. The one thing this system makes hard is seeing a request that spans five services and two brokers. Add OpenTelemetry tracing that propagates a trace context through the gRPC calls and across the Kafka/RabbitMQ envelopes (carry the trace id on events.Event), so a single order’s whole journey — gateway → Order → outbox → Kafka → Payment → saga & Notification → worker — is one connected trace. Layer on Prometheus metrics (consumer lag, retry counts, breaker state from Resilience →) and structured logging with the trace id, and the black box becomes legible.

  • A real payment gateway with persisted idempotency. This is the callout Process & Publish → made explicit. Replace the TotalCents rule with an actual card-network call, fronted by a payments table with a unique constraint on order_id (or an explicit idempotency key sent to the provider), checked before charging — so a redelivered order.created finds the existing row and returns the stored result instead of charging twice. This is the difference between “idempotent because it’s pure” and “idempotent because it remembers.”

  • Tighten delivery guarantees. Both broker hops are at-least-once today. Explore the transactional-outbox refinements that get you closer to effectively-once processing, and give the Notification worker a real retry-with-delay path (the TTL+DLX cycling or retry-count header Acks, Retry & Dead Letters → described but didn’t build), so a transient provider outage retries automatically instead of dead-lettering on the first failure.

  • Distributed compensation and an orchestration saga. The choreography saga → you built shines for a two-hop loop, but its weakness — the sequence isn’t written down anywhere — grows with each step. Add a step that can fail after an earlier one succeeded (reserve inventory, then charge, then ship) and you’ll feel the pull toward an orchestrator: a service holding an explicit state machine per saga, issuing commands and running compensations when a later step fails. Building it is the best way to understand the trade-off this course only described.

  • Event and contract versioning. As the system evolves, events.Event payloads and the .proto contracts The Contracts → will need to change without breaking existing consumers. Practice additive schema evolution, a schema registry for the Kafka events, and how a consumer tolerates a field it doesn’t recognize.

  • Autoscaling the workers. The Notification worker is a competing-consumers design The Send Worker → precisely so you can run more of them. Wire a Kubernetes HPA — on CPU, or better, on RabbitMQ queue depth — so the worker fleet grows under a backlog and shrinks when it clears, and watch the work redistribute automatically.

  • CI/CD. Automate the loop this course ran by hand: test Testing →, build the images Docker & Compose →, and roll the Helm release Kubernetes → on every merge, with the tests as the gate.

  • A real notification provider. Swap the worker’s log line for an email/SMS integration, and build the recipient resolution Consuming events → left as a stub — a lookup from order_id to a customer’s contact details, or a customer_id carried on the event from the start.

You started at an architecture diagram and a set of deliberate trade-offs, and you built every box in it — then hardened, tested, containerized, and deployed the whole thing. The system that felt like a lot of moving parts at the start is now one you can reason about hop by hop, extend along its own seams, and defend decision by decision. This course’s lessons — every code block you copied into your own shopmicro/ module along the way — live at github.com/avetavos/realworld-shopmicro; there’s no separate pre-built application repo to fork, because the point was building it yourself, one module at a time. Take the project you just assembled, break it, and build the next box yourself.

That’s the course. Go build something real.