Modular Monolith, Microservices, and Events: How to Combine Them Without Overengineering

Compare deployment boundaries, operational costs, and communication models to evolve from a modular core toward selective services and events.

9 min

Póster de arquitectura de software que conecta un núcleo modular, servicios selectivos y un flujo de eventos alrededor del número tres

Architecture is not chosen to make a diagram look sophisticated. It is chosen so the product can change without turning every delivery, failure, or business decision into a larger problem.

Modular monoliths, microservices, and event-driven architecture appear constantly in modern systems, but they are not three equivalent boxes. The first two define where boundaries live and deploy; events define how some parts communicate.

The right architecture minimizes the total cost of changing, operating, and understanding the system.

01. Three patterns that can coexist

Separate three decisions before choosing:

Three independent architecture decisions: code, deployment, and communication boundariesThree independent architecture decisions: code, deployment, and communication boundaries

DecisionQuestionCommon options
code boundarieswhich part may know which?modules, layers, ports and adapters
deployment boundarieswhat can ship independently?modular monolith, services
communicationmust the response be immediate?synchronous call, message, event

A monolith can publish events. A microservice ecosystem can use synchronous calls. A mature product can combine a modular core, two extracted services, and several asynchronous consumers.

The question is not which pattern “wins,” but how much independence each part actually needs.

02. Modular monolith: one deployment, explicit boundaries

A modular monolith keeps one deployable unit and divides the domain into modules with clear contracts. orders, catalog, billing, and identity may share a process and repository without freely sharing internal logic.

AdvantageWhy it matters
coordinated changesone refactor can cross modules in a single delivery
simple transactionsmany rules can run in one database transaction
compact operationsfewer pipelines, networks, credentials, and failure points
fast feedbackthe team learns the domain without freezing premature boundaries

It is a strong starting point while the product changes quickly, the team is small or medium-sized, and no operational reason justifies distribution yet.

The risk is not being a monolith; it is losing modularity. Cross-boundary imports, tables used as APIs, and changes that touch the entire system mean the boundaries exist only in the diagram. Architecture tests, ownership, and internal contracts should protect them.

03. Microservices: independence with an operational tax

A microservice owns a specific capability, can deploy independently, and controls its contract and data. Separating processes without separating ownership, deployment, or persistence only creates a distributed monolith.

Extraction makes sense when a verifiable need appears:

Selective extraction of one capability from the modular core and its new operational costSelective extraction of one capability from the modular core and its new operational cost

  • teams must deliver without coordinating every release;
  • workloads have clearly different scaling profiles;
  • specific availability, security, or isolation requirements exist;
  • change cycles no longer fit the main deployment.

Every service adds network, latency, authentication, versioning, observability, retries, and partial failure modes. Before multiplying them, the team needs deployment automation, metrics, correlated logs, tracing, versioned contracts, and operational ownership.

The correct unit is not “one table per service” or “one endpoint per service.” It is a business capability that can evolve and fail with enough independence.

04. Event-driven: decouple when reactions happen

Event-driven architecture publishes facts that already occurred: OrderPlaced, PaymentConfirmed, or ArticlePublished. Consumers react without the producer knowing about them or waiting for their responses.

Event-driven flow with producer, consumers, idempotency, retries, and a dead-letter queueEvent-driven flow with producer, consumers, idempotency, retries, and a dead-letter queue

This is useful for notifications, analytics, synchronization, long-running workflows, and traffic spikes. Events can connect modules inside a monolith or separate services.

Synchronous callAsynchronous event
the caller needs a response nowthe producer announces a fact
the flow is direct and easy to followseveral consumers react independently
receiver availability affects the requesta queue can absorb temporary unavailability
immediate consistency is simplereventual consistency must be accepted and designed

Events do not remove coupling; they move it into the message contract. Production use requires idempotency, bounded retries, a dead-letter queue, versioning, observability, and explicit policies for ordering and duplicates.

05. A hybrid architecture, step by step

Imagine a commerce platform that begins as a modular monolith. orders, catalog, and identity live in one deployment, each behind its contract.

When search needs its own indexing and scale, it is extracted as a service. When an order is confirmed, the orders module commits the transaction and publishes OrderPlaced. Inventory, email, and analytics process that event independently.

The result is not a complete migration to microservices. It is a deliberate composition:

PartPatternReason
transactional coremodular monolithconsistency and rapid change
searchindependent servicespecialized infrastructure and scale
notifications and analyticsevent consumersthey must not block checkout

This model extracts only where benefits exceed costs while keeping parts that do not need autonomy simple.

06. Decision matrix

Dominant signalStart or continue withRequired condition
domain is still changingmodular monolithenforceable internal boundaries
coordinated deployments slow teamsselective microservicesownership and operational platform
one workload needs separate scale or isolationindependent servicemetrics that prove the difference
secondary processes block the usereventsidempotency and retries
several consumers react to the same facteventsversioned contracts and traceability
distributed transactions are frequentreconsider the boundarydomain cuts are probably wrong

Do not use code size as the primary criterion. Evaluate team autonomy, rate of change, isolation, consistency, latency, and operational capability.

07. A safe evolution path

Measured evolution from a modular monolith toward one extracted service and event consumersMeasured evolution from a modular monolith toward one extracted service and event consumers

  1. Model capabilities and protect boundaries inside the codebase.
  2. Keep one deployment while coordination remains cheap.
  3. Instrument latency, failures, ownership, and change frequency.
  4. Extract one capability when measurable pain exists, not in anticipation.
  5. Add events where reactions can be asynchronous and tolerate eventual consistency.
  6. Revisit whether each boundary still reduces complexity.

Hexagonal architecture helps keep the domain independent from frameworks, databases, and transport inside either a monolith or a service. We develop it further in Hexagonal Architecture for Modern Web Applications.

The mature decision is usually less dramatic than the diagram suggests: start modular, distribute only capabilities that need independence, and use events when reaction time should also be decoupled.


SESSION_ELAPSED00:00:00
LOCALE: ENENV: PROD