If a small business application is becoming difficult to change, splitting it into several applications can sound like a clean solution.
Sometimes it is. Sometimes we’ve taken an untidy kitchen, put each station in a different building and introduced a radio system. The organization has improved on paper. Getting a sandwich out now involves weather conditions.
A modular monolith offers another option: one deployed application with deliberately separated responsibilities inside it. Milan Jovanovic’s March 2024 explainer provides a timely reason to consider that approach before assuming separate services are the next stage of maturity.1
Let’s test the choice against a fictional booking system.
Draw one booking from request to confirmation
The system checks availability, records a booking, takes payment and sends a confirmation. Each responsibility needs a clear owner. That does not automatically mean each needs a separate deployment.
A useful architecture review follows the change and asks where information is read, where it is updated and what happens when a step fails.
Separate responsibilities without moving every conversation onto the network
A modular monolith might have booking, payment and notification modules with defined interfaces. The team can restrict how those modules interact while running the application as one unit.
That is different from a large codebase where everything reads and changes everything else. Putting folders around the confusion won’t create boundaries, although the file tree may look much more employable.
Define which module owns a rule and its data. Other parts of the application should use agreed interactions rather than quietly modifying another module’s internals.
Jimmy Bogard’s vertical-slice approach can help organize individual use cases within a suitable architecture.3 It addresses how related feature concerns are grouped; it isn’t a requirement to choose either a monolith or microservices.
The practical benefit is understanding what a change touches. A cancellation should have a traceable path, whichever deployment model you choose.
When off-the-shelf stops fitting, see how we approach custom development.
Ask what independent services would actually buy
Separate services can support independent deployment, distinct scaling needs and clearer ownership across teams. Those are meaningful advantages when the business and operating team need them.
They also introduce network calls, separate failure modes and more work around diagnosis, releases and data consistency. A payment service can be unavailable while the booking service is working normally. The overall customer journey still needs an answer.
For the fictional system, ask whether one responsibility genuinely needs a different release schedule or capacity profile. Also ask who will monitor it and resolve a partial failure.
If the answer is that everything still has to change and deploy together, the separation may be adding coordination without delivering much independence. That’s worth knowing before the radios arrive.
| Modular monolith | Separate services | |
|---|---|---|
| Deployment | One application, released as a unit | Independent deployments for each service |
| Boundaries | Modules with defined interfaces inside one codebase | Separate applications that talk over the network |
| What can go wrong | Dependencies creep across modules and make later extraction harder | Network calls, partial failures and coupling through shared data or synchronised releases |
| A good fit when | A smaller team maintains one business application | Genuinely independent teams need different release schedules or capacity |
Look at the operating team, not just the diagram
Martin Fowler’s Monolith First discussion argues that service boundaries can be difficult to identify early and that managing distributed services carries a premium.2 It also acknowledges that the choice isn’t universal.
Check out this blog: Laravel or .NET? Start with the business, then have the argument.
I agree with the need to connect architecture to the team’s actual circumstances. A smaller team maintaining one business application may benefit from clear internal modules and a simpler deployment. A larger organization with genuinely independent teams may have stronger reasons for services.
Neither approach removes the need for discipline. A modular monolith can accumulate dependencies that make later extraction difficult. Microservices can become tightly coupled through shared data and synchronized releases.
Zulu Shack recommends choosing the simplest operating model that meets the demonstrated needs, while keeping business boundaries explicit. Extract a service when the benefit is concrete enough to justify running it.
For the booking system, that may mean building and improving one well-organized application first. Record the assumptions and revisit them when real usage, team structure or release constraints change.
You can have a thoughtful architecture without distributing it across the postcode. Sometimes the most useful boundary is the one the team can explain and maintain together.