A cohesive modular building beside separate connected services
Conceptual illustration

Microservices can support independent ownership, deployment, and scaling. They also move ordinary program behavior across network and data boundaries. A design decision that makes one kind of change easier can make a business transaction, failure investigation, or local development session harder.

The useful question is which boundary the system needs now. Counting services, predicting a future company size, or copying another organization's diagram does not answer it.

A deployment boundary changes the operation

Imagine an illustrative ordering application with orders, inventory, and billing. In one database transaction, it may be possible to enforce a set of local invariants together. Splitting those modules into separate services does not preserve that transaction automatically. A network call can time out after a remote update has already happened.

The design must now define the meaning of a partially completed order. Is stock held until billing succeeds? How is a failed payment reconciled? Which service is allowed to cancel an operation, and how are repeated messages recognized? These are business rules as well as infrastructure concerns.

If the team has not agreed on those rules, a service split can make uncertainty harder to change by turning it into a public contract.

Look for pressure that a service can actually relieve

A good candidate boundary has a concrete reason for independent operation. One capability may have a distinct workload, a separate release cadence, a specialized runtime, or an ownership model that repeatedly conflicts with the current deployment process. Those pressures deserve investigation.

Ask for an example of a recent change that the proposed boundary would have made easier. Then describe the additional coordination it would have required. If every ordinary feature still needs synchronized releases across all services, the diagram may have distributed deployment without achieving much independence.

Martin Fowler describes the extra operational and coordination effort as the microservice premium. His monolith-first discussion is a useful starting point for treating that effort as a cost to justify, rather than an automatic sign of architectural maturity.

Use a modular monolith deliberately

A single deployment can still have meaningful boundaries. Keep domain operations behind explicit interfaces, make data ownership visible, and avoid letting unrelated modules write directly into each other's internals. Tests should describe the contract between modules, not require every caller to know their storage details.

For the ordering example, inventory can expose a reservation operation rather than a collection of tables for the order module to mutate freely. Keeping that interface inside one process makes it less expensive to refine while the business rules are still changing.

This does not guarantee a painless future extraction. It gives the team a boundary to evaluate before it also owns network failures, versioned messages, and independent recovery.

Check readiness before extracting

An independently deployed service needs more than its own repository. Someone must own its runtime behavior, alerts, access controls, versioned contracts, deployment, and incident response. The consuming services need explicit timeouts and a policy for unavailable dependencies.

  • Can the service be released without requiring simultaneous changes everywhere else?
  • Does it own its data through a contract that other modules can respect?
  • Can the team trace one user operation across the new boundary?
  • Are partial completion and recovery defined in business terms?
  • Is there a supported way to develop and test callers without reproducing the whole production environment?

A missing answer is not a permanent argument against the split. It is work that belongs in the cost estimate before the split is presented as a shortcut.

Extract one capability and keep a way back

Choose a bounded capability and establish the existing behavior before moving it. Route traffic gradually where the system permits it, define which store is authoritative during migration, and verify that old and new application versions can coexist for the required rollout window.

A rollback is especially important when the extraction also changes data ownership. Reverting application code does not reverse data already written under a new model. A rehearsed migration plan is more useful than a promise that the new service can simply be switched off.

Compare topology without confusing it with evidence

The Architecture Trade-off Simulator includes a monolith-versus-microservices example. Use it to inspect the effect of extra network visits and constrained stations. It does not model team ownership or every distributed failure mode, so its result cannot choose the architecture for you.

Choose services when the independence they provide is valuable enough to support their responsibilities. Keep a cohesive deployment when it makes important changes easier to understand and operate. Both choices can be thoughtful engineering.