Start with a modular monolith. Split later, if ever.

Photo by Xavi Cabrera on Unsplash

Microservices solve a real problem: many teams working on one product without stepping on each other. If you have that problem, they can be worth the cost. Most teams I meet don’t have it yet. They have five developers, one product and a deadline.

The hidden bill of going distributed

Splitting a system into services moves complexity from your code into your infrastructure. Function calls become network calls that can time out. One database becomes several that drift out of sync. A stack trace becomes a distributed trace across six dashboards. Local development needs a Docker Compose file the size of a novel.

None of that delivers value to a user. It is the price you pay for independent deployments, and it only pays off when independence is what slows you down.

What “modular” actually means

A modular monolith is one deployable application, built from modules with hard boundaries. In .NET that can be as simple as one project per business capability, such as orders, billing and customers, with a few rules:

  • Each module owns its own data. No other module touches its tables.
  • Modules talk through a small public interface or through events, never through each other’s internals.
  • The rules are enforced by the compiler or an architecture test, not by good intentions.

That last rule matters most. Boundaries that are only in a diagram disappear the first busy sprint.

You keep the option to split

The nice thing about clean module boundaries is that they are exactly the seams you need if you ever do move to services. If billing really needs to scale on its own, or a separate team takes it over, you lift one module out. Its data is already separate, its interface is already defined.

Good boundaries are the hard part. Where you deploy them is the easy part. Teams that start with microservices often discover they drew the boundaries in the wrong place, and moving a boundary between services is far more painful than moving one between folders.

When I would split anyway

There are good reasons to start with more than one deployable: a component with wildly different scaling needs, a strict security boundary, or a piece written in a different language for a reason. Those are exceptions you can name. “Because Netflix does it” is not one of them.

Start simple, keep it modular and let real pressure tell you where to split. It is less exciting on a whiteboard and a lot more pleasant in production.

Keep reading