Skip to content

Microservices Architecture — When and How to Split (2026)

DodaTech Updated 2026-06-20 6 min read

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

How small should a microservice be?

A microservice should be large enough to be a meaningful bounded context and small enough to be owned by a single team (the "two-pizza team" rule). If a service takes longer than two weeks to rewrite, consider splitting it further.

What is the saga pattern in microservices?

The Saga pattern manages distributed transactions by breaking them into a sequence of local transactions with compensating actions for rollback. Choreography sagas use events; orchestration sagas use a central coordinator.

Should I start with microservices?

No. Start with a modular monolith. Create clear module boundaries from day one, then extract services as needed. This avoids the distributed complexity trap before you understand your domain.

How do microservices communicate?

Synchronous via HTTP/gRPC (request-response) or asynchronous via message brokers like Kafka/RabbitMQ (event-driven). Asynchronous communication is preferred for loose coupling and resilience.

What is a service mesh?

A service mesh (like Istio or Linkerd) handles inter-service communication at the infrastructure layer — load balancing, service discovery, retries, and observability. It separates these concerns from application code using sidecar proxies

Practice Questions

  1. What is a bounded context, and how does it relate to microservice boundaries?

  2. Compare choreography and orchestration — when would you choose each?

  3. Why should each microservice own its own database?

  4. List three signs that you should NOT use microservices.

  5. 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