Microservices Architecture Blueprint: Production Path
Five Docker containers with REST between them is not a production microservices system. Clients hit a load balancer and API gateway; services own their databases; async work flows through a broker; failures are contained with timeouts and circuit breakers; observability and CI/CD on Kubernetes keep the culture honest.
This blueprint walks the checkout request path and the supporting plane. For tool catalogs see Microservices Roadmap; for gateway, discovery, and CQRS depth see Microservices Design Patterns.
Clients, load balancer, and API gateway
Web, mobile, and partners should not know every service URL. Traffic lands on a load balancer, then an API gateway that routes, authenticates, rate-limits, and shapes requests.
Request trace (sync): Client → LB → Gateway (/v1/checkout) → Order service → (sync) Payment authorize → 201 with orderId. Keep the gateway thin — routing and cross-cutting concerns, not domain logic. Behind it: User, Order, Payment, Inventory, Notification.
Quick reference
- LB spreads connections; gateway is the logical front door.
- Gateway jobs: routing, auth, rate limits, request/response shaping.
- Clients talk to one host — not five service hostnames.
- Edges: Kong, Envoy, cloud API gateways, Ingress controllers.
- When not to: a single modular monolith with one deployable — a full gateway mesh is premature.
Remember this
A gateway that stays thin — routing and auth, never domain logic — is what lets an upstream timeout return a clean 504 instead of a client stuck learning five service hostnames by hand.
Services with database-per-service
Each microservice owns its data store — User DB, Order DB, and so on. No shared schema across teams. That decentralized data enables independent scale and deploy, at the cost of distributed transactions (use sagas/outbox instead of cross-DB joins).
Sync calls stay for paths that need an immediate answer. Prefer events so Payment does not tightly couple to Inventory’s uptime. When not to split a database: one team, one deployable, and JOINs are still your product’s core report path — stay modular until ownership forces a cut.
Quick reference
- One service → one database (logical ownership).
- No foreign keys across service databases.
- Compose reads via APIs or projections — not shared tables.
- Start with clear bounded contexts before splitting further.
- Related: Event-Driven Architecture for outbox/saga.
Remember this
Database-per-service buys independent scale and deploy at the cost of cross-DB joins — a saga or outbox replaces the transaction a shared schema used to give away for free.
Async, discovery, resilience, cache, and storage
The supporting plane makes the mesh survivable. A message broker decouples producers and consumers. Service discovery (or Kubernetes DNS) finds healthy instances. Circuit breakers isolate failures. Redis caches hot data. Object storage holds blobs outside OLTP databases.
Async hop on checkout: Order commits + outbox → broker order.placed → Inventory and Notification consumers. Failure path: Payment times out → breaker opens → Order returns a controlled error or queues a retry; Inventory never reserved on a half-finished sync chain if you designed the saga correctly.
Quick reference
- Kafka / RabbitMQ / Pulsar — async and event streaming.
- Eureka, Consul, ZooKeeper — or K8s DNS for discovery.
- Resilience4j — circuit break, retry, bulkhead (Hystrix is legacy).
- Redis — cache and sessions; S3/MinIO — blobs.
- Add broker when sync fan-out hurts; breakers when timeouts cascade.
Remember this
A circuit breaker's fallback should signal deferral through the outbox, not pretend success — add a broker when sync fan-out actually hurts, and breakers only once timeouts start cascading.
Cross-cutting: security and observability
Security at the edge is usually OAuth2 / JWT plus API hardening; service-to-service needs mTLS or mesh identity in serious setups. Observability is non-negotiable: metrics, logs, and traces across gateway → Order → Payment. Without traces, “the app is slow” stays guesswork.
See Observability: Logs, Metrics, and Traces for pillar mechanics. Design for failure and automate detection — that is culture as much as boxes.
Quick reference
- AuthN/Z: OAuth2, JWT, API security at the gateway.
- Metrics: Prometheus → Grafana (or cloud equivalents).
- Logs: structured, with trace ids.
- Traces: OpenTelemetry → Jaeger/Tempo/Zipkin.
- Alerts: on SLO burn and error rate, not only CPU.
Remember this
Without traces across gateway → Order → Payment, "the app is slow" stays guesswork — alerts belong on SLO burn and error rate, not only CPU.
CI/CD, Kubernetes, and environments
Delivery pipeline: Code → Build → Test → Containerize → Deploy → Monitor. Kubernetes adds rolling updates, health probes, and horizontal pod autoscaling. Promote the same image Dev → Staging → Production.
Zoom the data path once: Client → LB → Gateway → Service → Redis → Database, plus async via the broker. Version public APIs; secure service-to-service calls; never treat manual prod SSH as a platform.
Quick reference
- Same image promoted Dev → Staging → Prod.
- K8s: scale, heal, roll, discover.
- Automate tests and deploys.
- Version public APIs; break changes behind new versions.
- Health probes should match the gateway’s idea of “up.”
Remember this
Promoting the same image Dev → Staging → Production is what makes a health probe mean the same thing everywhere — manual prod SSH was never a platform, just a workaround wearing one.
Principles and when this blueprint fits
Principles worth enforcing: single responsibility, loose coupling, independent deploy, decentralized data, fault isolation, scale per service, observability. Benefits — team autonomy, faster deploys, isolated failures — only appear if ops and culture catch up.
When to use: multiple teams, clear bounded contexts, need independent scale. When not to: a small product still shipping as a modular monolith — extract when ownership and scale demand it, not when a slide demands Eureka on day one.
Quick reference
- Independently deployable > shared release train when teams multiply.
- Fault isolation requires timeouts, breakers, and bulkheads — not hope.
- Start modular; extract services when ownership and scale demand it.
- Related: roadmap for tools, patterns for deep dives, monolith comparison for timing.
Remember this
Fault isolation comes from timeouts, breakers, and bulkheads, not hope — extract a service when ownership and scale actually demand it, not because a slide put Eureka on day one.
Key takeaway
A production-ready microservices architecture is an edge (LB + gateway), domain services with owned databases, an async and resilience plane, full observability, and automated delivery. The hard part is operating small, observable, independently shippable units — not drawing the boxes once.
Practice (25 min): Draw checkout on one page: clients, gateway, three services, one cache, one broker hop, and where metrics/traces land. Mark the Payment timeout path (gateway 504 vs breaker open vs deferred outbox). Then read Microservices Design Patterns and Microservices Roadmap for the next layer of depth.
Related Articles
Explore this topic