Microservices Architecture — When and How to Split (2026)
In this tutorial, you'll learn microservices architecture — bounded contexts, service decomposition strategies, inter-service communication patterns, and the critical question of when NOT to use microservices. Why does this matter? Microservices are the most over-applied pattern in software engineering. Adopting them prematurely adds complexity without benefit. Real-world use: Amazon, Netflix, and Uber successfully use microservices at scale, but each spent years as a monolith first.
What Are Microservices?
Microservices architecture decomposes an application into independently deployable services, each owning its own data and domain logic. Services communicate over a network using lightweight protocols (HTTP, gRPC, or asynchronous messaging). Each microservice represents a bounded context from Domain-Driven Design (DDD).
graph TB
subgraph Client
Mobile[Mobile App]
Web[Web App]
end
API[API Gateway] --> Orders
API --> Inventory
API --> Users
API --> Payments
API --> Notifications
subgraph Services[Microservices]
Orders[(Orders
PostgreSQL)]
Inventory[(Inventory
MongoDB)]
Users[(Users
PostgreSQL)]
Payments[(Payments
DynamoDB)]
Notifications[(Notifications
Redis)]
end
Orders --> MessageBus[(Message Bus)]
Inventory --> MessageBus
Users --> MessageBus
Payments --> MessageBus
Notifications --> MessageBus
style Services fill:#4A90D9,color:#fff
style MessageBus fill:#E67E22,color:#fff
style API fill:#2ECC71,color:#fff
How Microservices Work
Service Decomposition
The most critical step is finding the right boundaries. Use DDD's bounded context to define service boundaries where domain concepts have consistent meaning:
# Order Service — bounded context for order management
class OrderService:
def create_order(self, user_id: str, items: list[OrderItem]) -> Order:
# Validates inventory via HTTP call to Inventory Service
inventory = InventoryClient.check_availability(items)
if not inventory.all_available:
raise InsufficientInventoryError()
order = Order(user_id=user_id, items=items)
self.order_repo.save(order)
self.event_publisher.publish(OrderCreatedEvent(order))
return order
# Inventory Service — bounded context for stock management
class InventoryService:
def check_availability(self, items: list[OrderItem]) -> AvailabilityResult:
stock = self.inventory_repo.get_all()
for item in items:
if stock.get(item.sku, 0) < item.quantity:
return AvailabilityResult(available=False)
return AvailabilityResult(available=True)
Inter-Service Communication
Two patterns dominate:
Choreography — services react to events without a central coordinator. When Order Service emits OrderCreated, Inventory Service consumes it and decrements stock.
Orchestration — a central orchestrator (like an API Gateway or Saga orchestrator) tells each service what to do and when:
# Orchestration: Order Saga Coordinator
class CreateOrderSaga:
def execute(self, request: CreateOrderRequest):
try:
result = self.inventory_service.reserve_items(request.items)
result = self.payment_service.charge(
request.user_id, result.total
)
result = self.order_service.confirm(
request.user_id, result.payment_id
)
self.notification_service.send_confirmation(request.user_id)
except PaymentFailedError:
self.inventory_service.release_items(request.items)
raise
Data Ownership
Each service owns its database. No service directly accesses another service's database. This enforces loose coupling but introduces eventual consistency challenges.
API Gateway
An API Gateway sits between clients and services, handling routing, authentication, rate limiting, and request aggregation:
sequenceDiagram
participant C as Client
participant G as API Gateway
participant S1 as Orders Service
participant S2 as Users Service
participant S3 as Payments Service
C->>G: POST /checkout
G->>S1: Create order
G->>S2: Get user profile
G->>S3: Process payment
S1-->>G: Order details
S2-->>G: User info
S3-->>G: Payment confirmation
G-->>C: Aggregated response
When NOT to Use Microservices
This is the most important section. Microservices are wrong when:
- Team size < 5 — Conway's Law means service boundaries match team boundaries. Small teams can't maintain multiple services.
- Simple CRUD — a monolith serves better for basic create-read-update-delete workflows.
- Startup phase — you need speed to validate product-market fit, not distributed debugging.
- No clear bounded contexts — arbitrary splitting creates distributed big ball of mud.
- Network overhead is unacceptable — latency-critical systems suffer from inter-service calls.
Pros and Cons
| Pros | Cons |
|---|---|
| Independent deployability — deploy services without coordinated releases | Distributed complexity — network failures, latency, partial failures |
| Technology diversity — polyglot persistence, different languages per service | Data consistency — eventual consistency, distributed transactions |
| Scalability — scale individual services independently | Operational burden — monitoring, logging, deployment complexity |
| Team autonomy — teams own services end-to-end | Debugging difficulty — tracing requests across services |
| Fault isolation — one service failing doesn't take down everything | Duplication — services duplicate shared logic or data |
Real-World Examples
Amazon
Amazon famously split its monolith into microservices in the early 2000s. Each team owns a service and communicates via well-defined APIs. The API Gateway handles authentication and routing.
Netflix
Netflix migrated from a monolith to microservices to scale globally. They pioneered patterns like the Circuit Breaker (Hystrix) and pioneered the API Gateway pattern.
Uber
Uber uses microservices organized by domain (trips, payments, riders, drivers). Each service is independently deployable and teams own their data stores.
When to Use Microservices
Use microservices when:
- Multiple teams need to work independently
- Different scalability requirements — one feature needs 100x more resources than others
- Multiple deployment cadences — some features release daily, others weekly
- Polyglot requirements — different services benefit from different languages or databases
FAQ
Related Concepts
- Modular Monolith vs Microservices — start modular
- Saga Pattern — distributed transaction management
- Event-Driven Architecture — asynchronous communication
- API Gateway Pattern — routing and aggregation
- CQRS Pattern — read/write separation per service
- Circuit Breaker Pattern — resilience and fallbacks
Practice Questions
What is a bounded context, and how does it relate to microservice boundaries?
Compare choreography and orchestration — when would you choose each?
Why should each microservice own its own database?
List three signs that you should NOT use microservices.
How does the API Gateway pattern simplify client-to-service communication?
Challenge
Take a monolithic e-commerce application. Identify bounded contexts (orders, inventory, payments, users, shipping). Design the service decomposition. Define the API contract for at least two services. Implement a simple choreography where "Order Created" triggers "Inventory Reserved".
Real-World Task
Analyze your current project. Is it a monolith or microservices? If a monolith, identify bounded contexts and design a migration path. If microservices, check each service for independence — can it be deployed without coordinated releases with other services? If not, refactor to enforce proper boundaries.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro