Skip to main content

Blog

Clean Architecture + Modular Monolith: Build systems that stay clear as they grow

Architecture·4 min read
← Back to Blog

A visual guide to protecting business rules with Clean Architecture — and scaling teams with a Modular Monolith before you jump to microservices.

Diagram of independent modules inside one application sharing a single database

Most products don’t fail because the first framework was the wrong choice. They fail because structure dissolves as the system grows: modules blur, business rules leak into controllers, and every change feels risky.

Clean Architecture and the Modular Monolith aren’t opposing camps. Used together they give a practical default: protect the business core, split the system into clear business areas, and defer distributed-system complexity until you truly need it.

What Clean Architecture protects

Clean Architecture is less about folders and more about a dependency rule: source-code dependencies always point inward. Outer rings may know about inner rings. Inner rings must not know about frameworks, databases, or how things communicate.

Domain

Business truth lives here

4

Frameworks & Drivers

UI · DB · APIs · Messaging

3

Interface Adapters

Controllers · Presenters · Gateways

2

Application

Use cases · Ports · Orchestration

1

Domain

Entities · Rules · Policies

The dependency rule: all arrows point inward.

At the center are the entities and policies that create product value. Around them are use cases, then adapters that connect to the outside world, and on the outside are frameworks, UI, and databases. If you swap Postgres for another store tomorrow, or REST for gRPC, the business logic barely needs to know.

Three quick tests:

  • Can you explain a business rule without mentioning NestJS, Laravel, Spring, or React?
  • Can you unit-test a use case with fakes and no real database?
  • If a table is renamed, does the domain language break, or only an adapter?

If the answer is no, the architecture is saying too much about infrastructure.

What a Modular Monolith looks like

A modular monolith is still one deployable application, but inside it is organized by business capability, not only by technical layer. Each module owns its API, application flow, domain model, and persistence adapter. Modules talk through explicit contracts and never reach into each other’s tables.

Modular Monolith

Single process · shared infrastructure · modular boundaries

1 deployable unit

Identity

  • API / Controllers
  • Application services
  • Domain model
  • Persistence adapter

Catalog

  • API / Controllers
  • Application services
  • Domain model
  • Persistence adapter

Ordering

  • API / Controllers
  • Application services
  • Domain model
  • Persistence adapter

Billing

  • API / Controllers
  • Application services
  • Domain model
  • Persistence adapter

Fulfillment

  • API / Controllers
  • Application services
  • Domain model
  • Persistence adapter

Reporting

  • API / Controllers
  • Application services
  • Domain model
  • Persistence adapter

Shared platform

Auth middleware · observability · messaging bus · database access helpers, never shared domain logic across modules.

One deployable. Many modules. Clear ownership.

For many products this is the sweet spot: enough structure for several people to work together, without paying the cost of dozens of services, network hops, and deployment pipelines from day one. Each module can be organized as a small Clean system of its own:

ordering/
  domain/        entities, value objects, domain services
  application/   use cases / ports
  adapters/      http, persistence, messaging
  ordering.module.ts

Calls across modules go through the application API or in-process messages, not one shared database model.

Keep the monolith or split into services

You don’t need microservices to look “modern”. You need boundaries you can trust.

Keep the modular monolith when:

  • It is one product, a few teams, and releases cut across many areas.
  • You need strong consistency between flows (checkout, billing, inventory).
  • You want fast local development and simple operations.

Consider splitting a service when:

  • One module needs to scale or run in a very different environment.
  • Its release cadence has to be fully independent of the rest.
  • Fault isolation is worth more than the network and observability cost.

A checklist for your next design review

  1. Name modules after the business, not the framework (say billing, not controllers-v2).
  2. Keep domain language pure: no ORM annotations, HTTP DTOs, or UI state in the core.
  3. Define each module’s public API and treat internal packages as private.
  4. Forbid cross-module database joins unless you accept the coupling knowingly.
  5. Add observability at module boundaries so you can see which module owns what in production.

In short, Clean Architecture keeps the rules honest and the Modular Monolith keeps the organization honest. Together they give you a system that is still readable in month three and still evolving in year three.

Designing a new platform, or untangling a codebase that keeps growing? MinTech can help choose boundaries that fit your product. Talk to MinTech.