Where Do You Draw the Boundary
Summary
Boundaries come from language, not from nouns. If two teams use the same word with different meanings, that is where the boundary is.
Why this was needed
"Let's split it into an order service, a product service, and a member service." It is the sentence that comes up most often in a meeting room and the design that fails most often. This approach merely promotes database table names directly to service names. The result is predictable. To create one order, you must call the product service and the member service each, and the three services always move together for one screen. Deployment independence does not arise.
The perspective proposed by domain-driven design is different. Boundaries come not from the kinds of data but from the language of the people who talk about that data.
How it works
Look at the same word, "product". To the sales team, a product is price, discounts, and whether it is shown. To the logistics team, a product is volume, weight, and storage temperature. To the settlement team, a product is a fee rate and a tax code. If you merge these three into a single product table, every time you add a column for a logistics requirement, the sales team's code is redeployed.
This is the point of the bounded context. Each context has its own product model, and contexts are connected only by an identifier (for example, the SKU). Translating the model when crossing a context boundary is the Anti-Corruption Layer we saw earlier.
There are three ways to find boundary candidates in practice. First, the points where the meaning of the same word diverges. Second, the range where a transaction is actually needed — data that must be in one transaction must be in one service. Third, change frequency — a part that changes every week and a part that changes once a year are likely to be different services.
What you meet in the field
The signal that a boundary is wrong appears first not in the code but in meetings. If deploying one feature always requires coordinating the schedules of two teams, the boundary is wrong. And the way to correct it is usually not to split the services further but to merge the two services back. Merge decisions are made far less often than split decisions, but are right far more often.
One more thing. In microservices, a "shared library" is a silent coupling. If you put a common DTO in one repository, then when you bump that repository's version, all the services must move together. It is safer to share contracts not by sharing code but by schema documents (OpenAPI, protobuf).
The actual procedure for finding boundaries
The phrase "find it in the language" is right but hard to carry out. There is a procedure used in the field.
Event storming. Gather domain experts and developers in one room and put "what happened" on the wall in the past tense. Order received, payment approved, stock deducted, shipping started. Then lay these events out in time order and mark who reacts to each event. The clusters where reactions gather are boundary candidates.
Count the coupling. Draw the candidate boundaries and count the calls in the actual code.
| Metric | Good value | What it means if bad |
|---|---|---|
| Number of services needed to render one screen | 1–2 | 3 or more means the boundary is wrong |
| Number of teams involved in deploying one feature | 1 | 2 or more means the boundary cuts across teams |
| Depth of synchronous calls between services | 1–2 | 3 or more means failures cascade |
Start with a monolith. If you split from the start, you are usually wrong, because the lines are drawn without knowing the domain. Draw boundaries as modules inside one repository (a modular monolith), and when those boundaries have not wavered for 6 months, then take them out as services. A wrongly drawn module boundary is moved in half a day, but a wrongly split service takes months.
When not to split
As much as drawing boundaries well, deciding not to split is also design. Under the following conditions, taking something out as a service is almost always a loss.
- There is one team. The benefit of microservices is the team's independent deployment. With one team, that benefit is absent and only the operational burden remains.
- A strong transaction is needed. If deducting inventory and creating an order must always succeed together or fail together, the moment you split them you have to build a saga and compensating transactions yourself. That complexity is greater than what you gain.
- You do not yet know the domain. In the first 6 months of a new product, the boundaries change every week.
Count the operating cost too. Adding one service adds one each of repository, CI, deployment pipeline, dashboards, alerts, and on-call documentation. A five-service system has not five times the code but five times the operational surface.
When data crosses a boundary
Once you draw a boundary, the problem "then how do we do joins?" comes right away. You must show product names in the order list, but the products are in another service. There are three answers, and each has a different value.
- Call and fetch. The simplest, but if the product service dies, the order list dies too. You multiply the availability of two services for one screen (99.9% × 99.9% = 99.8%).
- Replicate as much as needed. You embed the product name in the order as the value at the time of ordering. In fact this is also right in terms of the domain — even if the product name changes later, the name on past orders must stay the same.
- Build a read-only view. You subscribe to events and maintain a read-only table (CQRS). It is the most flexible but you must bear eventual consistency and a rebuild procedure.
There are more cases than you would think where the second is the right answer. If you first decide whether to view it as a "reference" or as "a fact at that time", the answer follows.
What to check in the next quiz
This module covers only concepts. With the quiz that follows, you check the criteria for judging boundaries, and in the next module you confirm by hand what the services split this way pay when they actually communicate.