Microservices Architecture Patterns with Spring Boot, Kotlin, and Gradle
A practitioner's field guide to the patterns behind real microservices systems — decomposition, data ownership, messaging, resilience, and delivery — built one working Kotlin service at a time, with honest trade-offs at every step.
Chapters
Modular monolith as a starting architecture
Why most teams should start microservices projects as a modular monolith, and how to enforce module boundaries in Kotlin and Gradle so the split later is a refactor, not a rewrite.
Microservices decomposition by business capability
How to split a modular monolith into real services along business-capability boundaries instead of technical layers, and why bounded contexts beat entity-based splits.
Database per service
Why splitting deployables without splitting databases only gets you halfway, and how to move from schema-per-service on one Postgres instance to a real database per service.
API gateway and backend for frontend
How to stop every client from calling three services directly, using a shared API gateway for cross-cutting concerns and a backend-for-frontend where clients genuinely differ.
Service discovery and centralized configuration
Why hardcoded service URLs and per-service config files stop working as Northwind scales past a handful of instances, and how discovery and config servers solve it.
Synchronous communication: REST and gRPC
When to keep REST for service-to-service calls and when gRPC's typed contracts and binary framing actually earn their added build-tooling complexity.
Event-driven architecture
Replacing Northwind's synchronous order-placement chain with Kafka events, and why that trades immediate consistency for availability and looser coupling.
Publish-subscribe and event notification
The difference between a thin event notification and a fat event-carried payload, and how that one design choice determines every consumer's coupling to the publisher's schema.
Event sourcing
Storing an order's history as an append-only event log instead of a mutable row, and why Northwind adopts it for one service, not the whole platform.
CQRS: command query responsibility segregation
Splitting order-service's write model from its read model so a slow reporting query stops competing with checkout traffic for the same tables.
Saga: choreography and orchestration
Closing the stock-reservation/payment consistency gap this series has carried since Chapter 7, comparing an event-driven choreographed saga against an explicit orchestrator.
Transactional outbox and inbox / idempotent consumer
Closing the gap between committing a local transaction and actually publishing its event, and the consumer-side discipline that makes redelivery safe.
Strangler Fig migration pattern
Using the API gateway from Chapter 4 to incrementally route traffic away from Northwind's legacy PHP monolith, one capability at a time, without a big-bang cutover.
Anti-corruption layer
Formalizing the translation boundary between order-service's clean domain model and the legacy monolith's tangled customer schema, so the legacy model never leaks in.
Sidecar and ambassador patterns
Pulling TLS, mTLS, and the legacy-monolith proxy logic out of order-service's own code and into deployment-level companion containers instead.
Circuit breaker, retry, timeout, fallback, and bulkhead
Using Resilience4j to stop a slow inventory-service from taking order-service's whole thread pool down with it, the exact failure this series predicted back in Chapter 7.
Cache-aside and distributed caching
Caching inventory-service's hottest stock lookups in Redis without ever serving a customer a phantom sold-out or in-stock result during a flash sale.
Competing consumers and work queues
Scaling notification-service's email sending horizontally by having multiple instances compete for work from one Kafka consumer group, instead of each getting its own copy.
Dead-letter queues and retry topics
What notification-service should do with a confirmation email that keeps failing to send — without blocking every order behind it or retrying forever.
API composition and aggregator services
Formalizing the fan-out-and-merge technique mobile-bff used back in Chapter 4, with a principled partial-failure policy instead of failing the whole request on one slow dependency.
Service mesh and zero-trust service communication
When Northwind's six hand-configured Envoy sidecars from Chapter 15 finally justify replacing manual configuration with a mesh control plane.
Observability: logs, metrics, traces, and correlation IDs
Wiring OpenTelemetry through Northwind's gateway, BFF, and services so a slow checkout can actually be traced to its cause, instead of guessed at from six separate log streams.
Contract testing and consumer-driven contracts
Replacing order-service's hand-written WireMock stub of inventory-service with a Pact contract that inventory-service's own CI verifies against its real implementation.
Blue-green, canary, and feature-flag-based deployment
Rolling out inventory-service's rewritten reservation algorithm to 5% of flash-sale traffic first, with a fast, one-command rollback if it misbehaves.
Kubernetes-native microservice design
Pulling together every Kubernetes primitive this series has used piecemeal since Chapter 1 into one coherent picture of what makes a service actually Kubernetes-native, not just deployed on it.
Security patterns: OAuth2/OIDC, JWT validation, RBAC/ABAC, and token propagation
Implementing the JWT validation this series deferred since Chapter 4, plus deciding what happens to a customer's identity as their request crosses order-service, inventory-service, and the legacy ambassador.
Multi-tenancy patterns
Northwind Platform's white-label expansion: what happens to database-per-service, the JWT claims from Chapter 26, and every downstream call once 'tenant' becomes a first-class concept.
Workflow orchestration with BPMN versus distributed sagas
Northwind's returns-and-refunds process needs a human approval step and a two-week waiting period — deciding whether that still fits the saga orchestrator from Chapter 11, or needs a BPMN engine instead.