A pattern for finding boundaries before splitting services
A structure for finding boundaries before splitting services that has survived marketplace operations and four sectors that share none of its vocabulary.
A pattern earns its keep by being cheaper to apply than to argue about. This one has survived marketplace operations and four other sectors that share none of the same vocabulary, which usually means the shape is real and not a coincidence.
A service boundary in the wrong place costs more than no boundary at all. Find the seams in the domain while everything is still one deployable, and only then decide whether any of them are worth a network hop.
SEAMS ARE FOUND, NOT CHOSEN
The seams in a system are wherever two parts of the business genuinely change on different schedules. You can't pick those from an architecture diagram. You find them by watching which files move together in six months of commits, and by noticing which conversations never involve the same two people.
Draw the boundary somewhere else and you've created a distributed transaction where a function call would have done. Every feature after that pays a tax: two deploys, a migration on each side, and a failure mode where half the change is live.
MODULES FIRST, NETWORK LATER
Enforce the boundary inside one codebase first. Separate modules, no shared tables, communication through an explicit interface. That gets you almost all the design benefit and none of the operational cost, and it's cheap to move if you drew the line in the wrong place.
If the boundary survives a year of features without anyone needing to reach across it, it's real, and you can consider a network. Most don't survive, which is exactly the information you wanted before committing to a deployment topology.
It paid for itself the first time a suspended seller kept transacting through a second account. Because the structure was already there, recovery was a query instead of an investigation, and trust and safety reviewers heard it from the system rather than from a customer.
WHAT ACTUALLY JUSTIFIES A SPLIT
There are honest reasons: independent scaling of something genuinely heavy, a compliance boundary that has to be physical, a team that needs to ship without coordinating. Notice that two of the three are organisational. That's normal and worth saying out loud.
"Microservices" as a goal isn't a reason, and neither is a diagram that looks tidier. If you can't name which of the three you're buying, you're paying the cost for nothing.
At a two-sided equipment marketplace it turned a recurring escalation into an ordinary state the system could describe. Close to 80,000 listings a month went through without a single manual reconciliation, which hadn't been true of any quarter before it.
WHERE IT GOES WRONG
- Distributed transactions across a seam that a function call handled fine last year.
- A boundary drawn from the org chart, requiring a two-service deploy for every ordinary feature.
- Two services sharing a database table, which is one service with extra latency.
- Splitting for scale on a component that turns out to be one percent of load.
Enforce the boundary as a module first. Earn the network hop later.
WHEN TO REACH FOR IT
It's cheap on day one and expensive to retrofit, which makes it a default rather than a decision. We put it in the first commit and haven't regretted it yet.