Decomposition strategies
How to find service boundaries: decompose by business capability or by subdomain, and keep coupling low.
key idea / coordinatorservice / good pathin progress / watch outproblem / failureneutral
What is a service?
A service has a public API of commands (change data), queries (read data) and events (announce changes). Its database is private. Size does not matter much: a good service is one a small team can build and change without waiting on others. Share libraries only for things that rarely change, like a Money class.
Information hiding, cohesion and coupling
Building Microservices treats a microservice as another form of modular design, one with a network between the modules. So old ideas about good modules still apply. David Parnas showed that modules pay off only when their boundaries hide details. In his words, the connections between modules are the assumptions they make about each other, so make fewer assumptions.
Cohesion is the relationship between things inside a boundary. Coupling is the relationship between things across it. A microservice is only independently deployable if its boundary is stable, and that needs strong cohesion and low coupling together.
Four kinds of coupling
Not all coupling is bad, but some kinds are far worse. Building Microservices ranks them using the fictional MusicCorp shop.
| Kind | MusicCorp example | What to do |
|---|---|---|
| Domain | Order Processor calls Warehouse to reserve stock and Payment to take money | Unavoidable. Many downstream calls hint that one service does too much. |
| Pass-through | Order Processor sends a Shipping Manifest to Warehouse only so Warehouse can forward it to Shipping | Let Warehouse build the manifest itself, so Shipping changes stay hidden. |
| Common | Order Processor and Warehouse both update the status column of one Order table | Give one Order service the table. It treats requests as requests and can reject bad transitions. |
| Content | Warehouse reads and writes the Order service's internal tables directly | Send requests to the Order service instead. |
The three-step process
Start from the requirements. Find the operations the system must handle, group them into services, then work out how services collaborate.
Strategy 1: business capabilities
A business capability is something the company does to make money. Capabilities stay stable even when the way of doing them changes, so services built around them stay stable too.
Strategy 2: DDD subdomains
Domain-driven design gives each subdomain its own model, called a bounded context. In practice you get nearly the same services as strategy 1.
The god class problem
In FTGO, Order is used by everything, so one class tries to do it all. DDD fixes this by letting each service keep its own small view of an order.
Domain-driven design in three words
The existing sections used DDD subdomains. Building Microservices highlights three of its ideas that matter most for service boundaries.
| Idea | Meaning | Why it helps |
|---|---|---|
| Ubiquitous language | Use the same words in code as the business uses | Gives APIs and events a shared vocabulary. A generic data model that calls every product an "arrangement" hides the real domain. |
| Aggregate | A cluster of objects managed as one unit, such as an Order | Keep one aggregate inside one service. Other services ask for changes, and the aggregate may refuse. |
| Bounded context | An explicit boundary that exposes some things and hides the rest | It is information hiding by design, so it makes a stable service boundary. |
The same idea can have different names and fields in different contexts. Finance keeps payments for a customer, while Warehouse keeps shipped goods for a recipient. A shared model can also expose less than the internal one, such as a stock count instead of shelf locations. A context can even be split further inside, as long as the outside world still sees one service.
Event storming
Event storming is a workshop for finding aggregates and bounded contexts, as described in Building Microservices. People who understand the business write what happens, then work toward the structure.
Orange notes are domain events (Order Placed, Payment Received). Blue notes are the commands that cause them. Yellow notes are the aggregates, found by looking at the nouns in the events. Finally the aggregates are grouped into bounded contexts, which often follow the company's structure. The result can feed an event-driven design or a plain request and response one.
Find the events first: event storming
Before designing schemas, find the events. Event storming is a workshop where developers and domain experts stick orange notes on a wall, one per business event in the past tense. From Practical Event-Driven Microservices Architecture, ch. 8.
The wall also shows where event schemas will need to be shared. Treat the output as a draft of your domain model, not as a finished design. Boundaries and aggregates are covered at the end of this lesson.
Other ways to find a boundary
The domain is a good starting point, but Building Microservices warns against one dogma. The goal you pick decides which driver matters.
- Volatility: pull out the parts that change most. It suits fast time to market but does little for a scaling problem. Beware fixed fast and slow labels such as "bimodal IT": they let hard-to-change parts stay untouched.
- Data: keep sensitive data in a few services. PaymentCo kept full card data in one zone so most of the system escaped the strictest audit.
- Technology: a part that needs a different runtime must be its own service. Do not use it as the general rule, or you rebuild the three-tier layers.
- Organization: a service owned by two teams invites delivery contention. A service can simply change owner when a team is reorganized.
Mixing is fine. A domain-shaped Warehouse might still be split further because one part must be written in a different language.
What gets in the way
Splitting is not free. These obstacles come back in later chapters.
Boundaries, aggregates and when to add a service
Code that changes together should stay together. Domain boundaries are usually the most stable choice, but the book names three more lenses you can combine with it. From Practical Event-Driven Microservices Architecture, ch. 3.
Domain-driven design supplies the vocabulary. A bounded context is the scope of one domain model, with its own ubiquitous language. It contains one or more aggregates, each modelling a domain concept. An entity is one instance. Commands change one aggregate and may be rejected; the change is announced as an event. An anti-corruption layer keeps one context's concepts from leaking into another.
Aggregate size is a performance decision. The book's example: beauty products have variants (sizes or colors) and categories.
Match the aggregates of your consumers where you can. If the stock service emits per-variant events but the discount service works per category, the consumer has to do a lot of work to relate them.
Finally, when you add functionality, either extend a service or create a new one:
| Add to the existing service when | Create a new service when |
|---|---|
| The domain concept is closely related and changes together (order and order lines). | The service already depends on many others and carries too much responsibility. |
| The new feature would otherwise share another service's database (a red flag; consume its event stream instead). | Several teams own the existing service with no clear ownership. |
| The service is anemic: a thin shell around a database with no domain logic. | It needs a different technology stack. |
| It needs to scale independently or has a different usage pattern. |
Recap
- A service hides everything behind its API and keeps its data private.
- Go from requirements to operations, then services, then APIs.
- Cut along business lines: capabilities or DDD subdomains.
- Give each service its own small model so no god class ties things together.
- Good boundaries hide information and keep related change together. Rank coupling: domain, pass-through, common, content.
- DDD gives the vocabulary: ubiquitous language, aggregate, bounded context. Event storming finds them.
- Volatility, data, technology and team structure can also drive a split. Split the data along the same lines.
- Event storming finds the events and commands before you design schemas.
- Draw boundaries by domain, then refine by team, change and data type. Size aggregates with consumers in mind.
Quick check
Show answer
Each service has its own model. The kitchen only cares about items, prepare-by time and status, so a separate, small Ticket class fits better than the huge shared Order.