Explore common patterns used in modular monolith architectures. Each pattern addresses specific challenges in building well-structured, maintainable systems.

Integration Events

The Problem

Modules need to react to changes in other modules without creating tight coupling. Direct method calls create dependencies that make the system rigid.

The Solution

Use integration events to communicate between modules. Modules publish events when something significant happens, and other modules can subscribe to react.

Benefits

  • Loose coupling - Modules don’t need to know about each other
  • Flexibility - Easy to add new subscribers
  • Auditability - Events provide a clear record of what happened
  • Path to microservices - Events translate well to message queues

Read more →

Module Boundaries

The Problem

In a monolithic application, it’s easy for code to become tangled. Without clear boundaries, dependencies grow organically and the system becomes difficult to change.

Module boundaries help prevent this by establishing clear contracts between parts of the system.

The Solution

Define modules based on business capabilities, not technical layers. Each module should:

  • Own its data
  • Expose a clear API
  • Hide its implementation details
  • Communicate with other modules through well-defined contracts

Implementation

There are several ways to enforce module boundaries in your codebase:

Read more →

Shared Kernel

The Problem

Modules need to share some common code—value objects, interfaces, and utilities. But sharing too much creates hidden coupling.

The Solution

Create a small, carefully curated shared kernel that contains only the most fundamental, stable abstractions.

Guidelines

Keep It Small

The shared kernel should be minimal. If it grows large, it becomes a source of coupling.

Keep It Stable

Changes to the shared kernel affect all modules. Treat changes with care.

Read more →