Microservices Architecture
Design systems as independently deployable services - covering service boundaries, communication patterns, API gateways, service mesh, and when NOT to use microservices.
skill: Microservices Architecture category: DevOps tags: [Microservices, Architecture, API Gateway, Service Mesh, gRPC] readingTime: 8
Microservices solve organizational scale problems (many teams, independent deployments) at the cost of operational complexity. For most applications and teams (< 20 engineers), a modular monolith is simpler, faster to build, and cheaper to run. Choose microservices for scale reasons, not because they sound better.
Workflow
- Define service boundaries with domain-driven designBad service boundaries (splitting too fine, splitting across wrong lines) create distributed monolith problems - tight coupling over the network. Bound services by business capability.
- Choose communication: REST vs. gRPC vs. eventsSynchronous REST is simple but creates coupling; gRPC is efficient for internal services; events decouple services and enable eventual consistency.
- Add an API gatewayAn API gateway is the single entry point for clients - it handles auth, rate limiting, routing, and protocol translation, so individual services don't have to.
- Handle distributed failures with circuit breakersIn microservices, network calls can fail. Without circuit breakers, one slow service cascades to all callers - causing system-wide failure.
- Implement distributed tracingA user request in a microservices system spans 5-15 services. Without tracing, diagnosing latency or errors means searching logs across all services with no clear thread.
Output Format
Microservices system: domain-based services, API gateway, async events, circuit breakers, and distributed tracing.
System Design:
├── API Gateway (auth, rate limit, routing)
├── Services (one per business domain)
│ ├── user-service (REST + JWT issuance)
│ ├── order-service (REST + publishes OrderCreated)
│ ├── payment-service (consumes OrderCreated via Kafka)
│ └── notification-service (consumes events, sends emails)
├── Message Bus: Kafka / AWS SQS+SNS
└── Observability: OpenTelemetry → Jaeger + Prometheus → GrafanaExamples
Input
Output
Edge Cases & Gotchas
- Shared database anti-pattern - Two services sharing a database are not microservices - they're a distributed monolith. Changes to the DB schema require coordinating both services.
- Service discovery - Hardcoded IP/hostname addresses break when services scale or restart. Use a service registry (Consul, Kubernetes DNS) so services find each other by name.
- Event ordering guarantees - Kafka partitions guarantee ordering within a partition. For events that must be ordered (UserCreated before UserUpdated), use the same partition key (userId).
- Versioning internal APIs - Internal gRPC or REST APIs between services also need versioning when the schema changes. Use protobuf field numbering for backward compatibility.
When to Break These Rules
- Start with a modular monolith - same code organization as microservices but single deployment unit. Extract services only when a module needs different scaling, language, or team ownership.
- Don't convert to microservices to solve performance problems - most performance issues in monoliths are DB, N+1 queries, or missing caches, not architectural.
Reference Files
https://microservices.io/patterns/https://martinfowler.com/articles/microservices.html