What I'm sure of is that it's distributed last
A response to the monolith-first debate arguing that both answers describe deployment, not design. The work that comes first is finding logical service boundaries; physical distribution is a separate, expensive decision best taken last, when scaling, compliance or release cadences make it worthwhile.
Key points
- Monolith-first and microservices-first both describe physical deployment, not logical design, so the choice comes too early.
- The expensive mistake is a misplaced boundary: it splits data that should change together, whether the gap is a namespace or the network.
- Design each service as if it were already distributed: own data, message-based contracts, eventual consistency with an outbox, composition at the edges.
- NServiceBus's new non-durable transport runs messaging in memory inside one process, so patterns can start before a broker is added.
- Reasons to split a deployment are operational or organizational: independent scaling, security isolation, failure isolation, release cadence or a different stack.
- Moving a component to its own host is a deployment change, not a rewrite, provided no service reads another service's tables.
The article is a practical sketch that the monolith-versus-microservices debate answers a premature question: both options only describe how the system is deployed, not how it is designed. The author argues that the real first step is finding logical boundaries, decided by which data belongs together and which capabilities change together, and he recommends the anti-requirements technique to test candidate boundaries with business stakeholders.
Once boundaries exist, modules should behave as if they were already distributed: each service owns its data, services talk through commands and events, consistency across services is eventual with an outbox, and cross-service data gets composed at the UI or a gateway. The NServiceBus non-durable transport lets teams use messaging in-process before adding a broker. Splitting a deployment is a last step, justified by scaling, compliance, failure isolation or release cadence, and it can be undone.
Real-world use
- An online store where Sales, Shipping and Payments are separate logical services in one host until payments must scale or be isolated for compliance.
- A team starting a new product that wants messaging patterns but no broker yet, using an in-memory transport inside a single process.
- A system whose read models need data from several services, composed in the UI or a gateway instead of joined in a shared database.
- A growing monolith where the release train is the bottleneck, so one component is moved to its own host without touching its handlers.
Why it matters
Getting boundaries right is the costly, hard-to-reverse decision, while deployment topology stays a knob: with clean boundaries you can move a component into its own process later without a redesign under pressure.