Skip to content

The Evolution from Monolith to Microservices

Introduction

No architecture is "the best" — there is only "the best fit for the current stage." Moving from monolith to microservices is not a single leap but a gradual evolution as business scale and team size grow. Splitting into microservices too early is just as dangerous as splitting too late.

What will you learn from this article?

After reading this chapter, you will gain:

  • Evolution Path: Understand the four stages from monolith to microservices
  • When to Split: Know when you should split and when you shouldn't
  • Splitting Strategies: Master the methodology of splitting by business domain
  • Communication Patterns: Understand the choices between synchronous and asynchronous inter-service communication
  • Data Splitting: Understand the challenges and solutions of database decomposition
ChapterContentCore Concepts
Chapter 1Architecture Evolution PathMonolith → Modular → SOA → Microservices
Chapter 2When and Why to SplitConway's Law, team autonomy
Chapter 3Splitting StrategiesDDD bounded contexts, Strangler Fig pattern
Chapter 4Service CommunicationREST, gRPC, message queues
Chapter 5Data SplittingDatabase decomposition, data synchronization

1. Architecture Evolution Path

Architecture evolution is not driven by technology — it is driven by organizational scale. When a team grows from 5 to 500 people, the collaboration efficiency of a monolithic architecture drops precipitously.

StageArchitectureTeam SizeCharacteristics
StartupMonolithic application1–10 peopleAll code in one project, simple deployment
GrowthModular monolith10–50 peopleCode organized by module, but still deployed together
ExpansionSOA (Service-Oriented)50–200 peopleSplit into coarse-grained services by business line
ScaleMicroservices200+ peopleFine-grained services, each team develops and deploys independently
Architecture Evolution Path
Click each stage to inspect its architecture characteristics
1
Monolithic architecture
2
Modular monolith
3
Service-oriented architecture
4
Microservices architecture
Monolithic architecture
All features are packaged in one application and share one database. It is simple and suitable for early rapid iteration.
User module
Order module
Payment module
Product module
Monolith app (one process)
MySQL
Suitable scale:Team < 10 people, DAU < 100k
Core challenge:Code is tightly coupled; a bug in one module may bring down the whole system

Conway's Law

"Organizations which design systems... produce designs which are copies of the communication structures of these organizations." — Melvin Conway

Simply put: 3 teams building one system will end up with 3 services. The essence of architecture splitting is organizational splitting.

Inverse Conway's Law: Since organizational structure determines system architecture, to get the architecture you want, first restructure the organization accordingly. For example, if you want an independent payment service, first create an independent payment team. Many companies fail at microservice splitting not because of technology, but because the organization didn't adapt.


2. When Should You Split into Microservices?

Not all systems need microservices. Splitting too early introduces unnecessary complexity.

SignalDescriptionRecommendation
Frequent deployment conflictsMultiple teams modifying the same codebase, frequent conflictsConsider splitting
A module needs independent scalingThe search module needs 10x the resources of other modulesConsider splitting
Differentiated tech stacks neededAI module uses Python, main site uses JavaConsider splitting
Team < 10 peopleLow communication overhead, monolith is sufficientDon't split
Business still in exploration phaseRequirements change rapidly, boundaries are unclearDon't split
No DevOps capabilityNo CI/CD, containerization, or monitoring infrastructureDon't split

3. Splitting Strategies

3.1 Split by Business Domain (DDD Bounded Contexts)

The Bounded Context from DDD (Domain-Driven Design) is the best guiding principle for splitting microservices. Each bounded context corresponds to an independent business domain with its own data model and business rules.

What is a bounded context? The same word can mean different things in different business domains. For example, "user" in the user domain means registration info (name, email), in the order domain means the buyer (shipping address, payment method), and in the recommendation domain means a behavioral profile (browsing history, preference tags). A bounded context draws a boundary within which terminology and models have a clear, unified meaning.

┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│  User Domain │  │ Order Domain│  │Payment Domain│
│             │  │             │  │             │
│ User        │  │ Order       │  │ Payment     │
│ Profile     │  │ OrderItem   │  │ Refund      │
│ Address     │  │ Cart        │  │ Transaction │
│             │  │             │  │             │
│ User Service│  │Order Service│  │Payment Svc  │
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │
       └────── API calls / event communication ───────┘
Bounded ContextCore EntitiesCorresponding Service
User domainUser, Profile, AddressUser Service
Product domainProduct, Category, SKUProduct Service
Order domainOrder, OrderItemOrder Service
Payment domainPayment, RefundPayment Service
Logistics domainShipment, TrackingLogistics Service

3.2 Strangler Fig Pattern

Don't rewrite the entire monolith at once. Instead, like a strangler fig, gradually replace old modules with new services:

  1. Create a new service outside the monolith
  2. Route some traffic to the new service through a proxy layer
  3. After verifying the new service is stable, gradually migrate more traffic
  4. Eventually replace the old module entirely

4. Service Communication Patterns

MethodProtocolCharacteristicsUse Cases
RESTHTTP/JSONSimple and universal, great ecosystemExternal APIs, CRUD operations
gRPCHTTP/2 + ProtobufHigh performance, strongly typedHigh-frequency internal service calls
Message queueAMQP/KafkaAsync decoupling, peak shavingEvent notifications, async tasks
GraphQLHTTP/JSONClient-driven queriesBFF layer, mobile clients

Synchronous vs. Asynchronous Choices

  • Need immediate results → Synchronous (REST/gRPC)
  • Don't need immediate results → Asynchronous (message queue)
  • One event triggers multiple actions → Asynchronous (publish-subscribe)

Rule of thumb: go async whenever possible. The longer the synchronous call chain, the more fragile the system.


5. Data Splitting: The Hardest Part

The most painful part of microservice splitting is not code decomposition — it's database decomposition. Each service should own its own database, but this makes cross-service queries difficult.

ChallengeDescriptionSolution
Cross-service JOINsCannot directly JOIN tables from two servicesAPI composition queries, data redundancy
Distributed transactionsCross-database transactions cannot use local transactionsSaga, local message tables
Data consistencyData across services may be temporarily inconsistentEventual consistency, event-driven
Data migrationMigrating from shared to independent databasesDual-write transition, data sync tools

Summary

Moving from monolith to microservices is a gradual process, not a overnight revolution.

Key takeaways from this chapter:

  1. Evolution Path: Monolith → modular monolith → SOA → microservices, each step driven by clear motivations
  2. When to Split: Team size, deployment conflicts, and scaling needs are signals to split
  3. Splitting Strategies: Use DDD bounded contexts to guide splitting and the Strangler Fig pattern for gradual migration
  4. Communication Choices: Go async whenever possible; keep synchronous call chains as short as possible
  5. Data Splitting: The hardest but most important part — accepting eventual consistency is the key mindset shift

Further Reading