A boundary becomes useful when it explains who can make a decision, which state that decision changes, and what other components may rely on.

Start with a rule that must hold

Consider a booking workflow with availability, reservations, and notifications. Splitting these nouns into separate services does not settle the difficult question: who decides whether the last available place can be reserved?

I would write that rule down before choosing a deployment shape. If two components can independently confirm the same capacity, naming one of them the owner does not resolve the competing authority.

Describe the boundary in operational terms

A short boundary description should identify the authoritative state, the commands that may change it, and the facts other components receive. It should also explain which observations may be stale. A notification view can often lag behind a completed reservation; the decision to allocate the last place has a different consistency requirement.

Designing around business capabilities rather than technical layers is also the starting point in Microsoft’s domain analysis guidance. I find the useful review question is whether a component owns a meaningful decision or merely forwards data between components that still have to agree on everything.

A contract needs failure behavior

A successful response shape is only part of an interface. The caller also needs to know what a timeout means, whether a command can safely repeat, and how to discover the final outcome. Events need equally clear meaning: “requested” and “completed” describe different facts.

For the booking example, I would distinguish an accepted request from a confirmed allocation in both the contract and the visible status. That avoids forcing consumers to infer completion from the arrival of an intermediate message.

Test the proposed separation

  • Can either side change an internal representation without a coordinated release?
  • Which business rule fails if a message arrives late or twice?
  • Can an operator identify the component responsible for an incomplete operation?
  • Does a normal request require several synchronous round trips just to make one decision?

These questions can justify keeping related behavior in one module. A module inside a monolith can have explicit ownership and a narrow interface. A separate process adds operational costs that need a reason: independent scaling, isolation, delivery, or another requirement that matters to the system.

My starting point is the responsibility we need to protect. The deployment boundary follows once the cost and benefit of separating that responsibility are clear.