Back to all posts
·5 min read

Microservices or a modular monolith? A practical framework for drawing the boundary

When a product begins to grow, the architecture conversation often opens with the same question: “Should we split this into microservices?” It is a fair question, but an incomplete one. A microservice is not a code organisation technique that creates more folders. It is a new operating model for deployment, data ownership, observability and team collaboration.

The useful starting point is therefore not “how many services should we have?” A better question is: Which business capability is genuinely ready to change and operate independently from the rest? If the answer is unclear, a modular monolith is often the faster, clearer and more reversible first shape.

What a modular monolith solves

A modular monolith has explicit domain modules within a single deployable application. Identity, search, ordering, notifications or document management can each have their own application rules and, as far as practical, their own data access. Calls between modules happen through defined interfaces rather than arbitrary access to another module’s tables.

“Monolith” does not mean an unstructured codebase. The aim is to keep the convenience of a single deployment while making future extraction boundaries visible now. The trade-off is that, when boundary violations go unnoticed, modularity can gradually become only a folder structure.

A modular monolith is not avoiding an architectural decision. It creates room to make that decision with better evidence.

Five questions that define a service boundary

Before turning a module into an independent service, answer the following questions with concrete examples.

1. Is there a clear owner for the business rule?

A service should own a meaningful business capability, not a technical layer. A notification module, for example, is more than code that sends email. It may own delivery preferences, retry rules, templates and delivery history. By contrast, “shared utilities” or “database access” are rarely useful service boundaries.

Naming is a good test: can the boundary be described as a verb or a business outcome? If not, the thing being extracted is probably not yet a domain.

2. Can data ownership be established without sharing?

In microservices, data is usually harder than networking. Each service should own its data; writing directly to another service’s tables is the shortest path to a distributed monolith.

Some flows can accept eventual consistency. After a record is created, an update to a search index or notification a few seconds later may not harm the user experience. Identity, authorisation or monetary state can demand a stronger consistency expectation. The business tolerance for error—not a technology preference—should decide the outcome.

3. Is the rate of change genuinely different?

If two modules change in the same release, by the same team and at the same frequency, separating them may not improve delivery speed. A separate service earns its cost when it has its own release rhythm, risk profile or independent publishing need.

Look at history rather than predictions. Which areas changed in the last few releases? Which changes waited for other modules to be tested, built or deployed? If a separate release cadence does not appear repeatedly, the service boundary may not be mature yet.

4. Are scaling and failure isolation real needs?

A module that consumes substantially more CPU, memory or network capacity than the rest is a good candidate for extraction. But “it might grow later” is not sufficient on its own. Measure first: make the bottleneck and its cost visible.

The same applies to failure isolation. If a heavy reporting job is delayed, does it affect sign-in? When a file-processing queue backs up, do the main flows still work? If the answer is yes, independent resource limits and different scaling policies can create clear value.

5. Can the team operate the service?

Every new service means a pipeline, configuration, secret management, health checks, logs, metrics, traces, alerts and version compatibility. This burden often lasts longer than the initial implementation effort. For a small team, creating three separate operational surfaces can easily outweigh the benefit of the business separation.

The number of services should grow with real team ownership. A technically possible boundary is not necessarily economical to separate today.

You do not have to wait to prepare for extraction

Choosing a modular monolith does not close the door to future services. When implemented deliberately, it creates a safe extraction path:

  • Collect the commands and queries a module exposes behind explicit interfaces.
  • Prevent other modules from reaching into internal data; the owning module should offer the information they need.
  • Define domain events beside the business rule, and consider patterns such as an outbox to make event publication reliable.
  • Make observability, authentication and error contracts consistent throughout the application.
  • When extraction time arrives, begin with a read model or asynchronous consumer; do not make a critical flow distributed in a single move.

These preparations are not valuable only for a later move to microservices. Even in one application, they reduce dependencies, make testing easier and help new developers understand the domain faster.

Signals that justify revisiting the decision

Architecture should not become a permanent label. It is meaningful to revisit service extraction when signals become regular: unrelated changes blocking each other’s release, a module becoming a persistent resource-hungry neighbour, distinct access or audit requirements emerging, or a team taking end-to-end ownership of a particular capability.

Moving to microservices before these signals often means buying distributed-systems problems early. Moving too late usually means the module boundaries were not made visible in the first place.

The durable approach is not to choose an architecture as a slogan. It is to justify every boundary with business value and operational cost. Microservices are a powerful tool at the right boundary; a modular monolith provides a safe working space for finding that boundary.