ApplicationExpert

How do you draw service boundaries?

“You are splitting a system into services. What makes a boundary good, what makes it bad, and how would you find the right one for orders, inventory and payments?”

What this tests

  • Boundary criteria: cohesion, change frequency, data ownership, team ownership
  • Recognition of chatty boundaries and distributed transactions as boundary smells
  • Domain-driven thinking without the vocabulary tour
  • Willingness to keep things together when the boundary is not clear

Answers by level

Read the beginner answer first and notice what is missing.

A good boundary is one where the things inside change together and the things across it change rarely. Signs of a good boundary: a small, stable interface; the service can complete its core operation without calling others synchronously; it owns the data it needs to answer its own questions. Signs of a bad one: a request crosses it five times, two services need the same transaction, or a schema change on one side forces a deploy on the other.

Splitting by entity is the common mistake. "Order" and "OrderLine" and "Inventory reservation" are one workflow — placing an order — and separating them creates a distributed transaction on the happiest path in the system. I would draw boundaries by workflow and ownership instead: order placement (orders plus inventory reservation), payment capture (its own regulatory and provider concerns, different failure behaviour), fulfilment. Payments is a good boundary precisely because it changes for different reasons — provider integrations, PCI scope — and its interface is small: authorise, capture, refund.

When the boundary is not clear, keep the code together in a module and wait until the change patterns reveal it.

Green flags · Red flags

Strong green flag · Checks the commit history to see which code actually changes together before proposing a boundary.
Green flags
  • Draws boundaries by workflow and reasons-for-change, not by entity
  • Uses cross-boundary transactions and call counts as smells
  • Ensures each service can answer its own questions from its own data
  • Mentions team ownership as a boundary input
  • Willing to defer a boundary that is not yet clear
Red flags
  • "One service per database table."
  • Proposes a boundary that requires a distributed transaction on the main path
  • Ignores the external payment provider's failure behaviour as a reason to isolate payments
  • Cannot explain what data each service owns

Follow-up questions

F1
The Order service needs product names for the order page. Call the Product service on every render?
F2
How do you avoid overselling if inventory is a separate service?
F3
What if two teams both need to change the same service constantly?

Scenario

A proposed split creates a Cart service, an Order service, an OrderLine service, a Pricing service and a Discount service. Placing one order calls all five synchronously and takes 900 ms with three failure points. Redraw the boundaries and explain what data each service must own for placement to be a single local transaction.

Learn this topic