Microservices vs modular monolith
Both styles divide a system into modules with clear boundaries. The difference is whether those modules are deployed and run separately (microservices) or together as one application (modular monolith).
At a glance
Section titled “At a glance”| Modular monolith | Microservices | |
|---|---|---|
| Deployment | One unit | Many independent units |
| Module communication | In-process function calls | Network calls and messages |
| Data | One database, separate schemas per module | A database per service |
| Transactions | Ordinary database transactions | Sagas and eventual consistency |
| Scaling | Whole application scales together | Each service scales on its own |
| Operational cost | Low | High |
| Team independence | Moderate: shared release train | High: independent releases |
| Cost of moving a boundary | Low: a refactor | High: a migration |
Choose a modular monolith when
Section titled “Choose a modular monolith when”- The team is small or medium-sized (roughly one to a few teams).
- The domain is still being discovered and boundaries may move.
- You don’t yet have mature deployment automation and observability.
- Strong consistency across modules matters, for example in financial postings.
Choose microservices when
Section titled “Choose microservices when”- Many teams are slowed down by a shared release process.
- Specific parts need independent scaling, availability or technology.
- Boundaries are well understood and stable.
- You can afford the platform: CI/CD per service, tracing, service discovery and on-call.
The verdict
Section titled “The verdict”Start with a modular monolith unless you have a concrete reason not to. Enforce module boundaries strictly (no reaching into another module’s tables or internals), and a module can later be extracted into a service when real pressure appears. Going the other way, merging badly split services back together, is far more painful.
Worked decision: extracting a payments module
Section titled “Worked decision: extracting a payments module”An illustrative scenario showing how the verdict above is applied in practice.
The situation
Section titled “The situation”A lending platform runs as a modular monolith with five modules: applications, underwriting, loan servicing, payments and notifications. Two years in, the payments module is under pressure:
- Payment processor integrations change monthly, but every change waits for the whole platform’s fortnightly release.
- Card and bank data handled by the payments module puts the entire application inside the scope of payment-security audits.
- Month-end collection runs create load spikes ten times normal traffic, all in payments.
- A dedicated payments team now exists, separate from the lending teams.
Weighing it
Section titled “Weighing it”| Signal | Present? | Points toward |
|---|---|---|
| Independent release cadence needed | Yes | Extract |
| Compliance or security scope to isolate | Yes | Extract |
| Distinct scaling profile | Yes | Extract |
| Dedicated owning team | Yes | Extract |
| Boundary stable for a year or more | Yes | Extract |
| Strong transactional coupling to other modules | Partly: a payment updates the loan balance | Stay, unless the coupling can be made asynchronous |
Five strong signals and one manageable obstacle. The coupling is handled by making the loan balance update asynchronous: payments publishes a PaymentSettled event, and loan servicing applies it idempotently.
The decision record
Section titled “The decision record”ADR-014: Extract payments into a separate service
Status: AcceptedContext: Payments needs independent releases, its own scaling and a reduced audit scope. Its module boundary has been stable for 18 months and a dedicated team owns it.Decision: Extract the payments module into a separately deployed service with its own database. Loan servicing consumes PaymentSettled events instead of sharing a transaction with payments. The other four modules remain in the monolith.Consequences: + Payments releases independently; audit scope shrinks to one service. + Collection spikes scale payments alone. - Loan balances become eventually consistent (seconds, not instant). - Requires event delivery guarantees, idempotent consumers and reconciliation between payments and loan ledgers.Alternatives considered: - Stay in the monolith with stricter module rules: doesn't solve audit scope or release cadence. - Split the whole platform into services: no other module shows the signals above.Revisit if: - Reconciliation finds unexplained mismatches between ledgers. - Most payments changes still need coordinated monolith releases. - Event lag regularly exceeds what customer service can explain.What made this the right call
Section titled “What made this the right call”The decision extracts one module for specific, observable reasons, and leaves the rest of the monolith alone. Each “revisit” condition is something the team can measure in production, so the decision can be checked rather than argued about.
Read more
Section titled “Read more”Written and edited by Abhinav Taduka, enterprise software architect.