Blog

Modular Monoliths: Bridging the Gap Between Monoliths and Microservices

09 Aug, 2024
Xebia Background Header Wave

Microservices architecture has revolutionised how we build software, offering significant advantages such as:

  • Better scalability
  • Technology flexibility
  • Fault isolation
  • Independent deployments

These benefits stem from the clear, physical boundaries between different domains, boosting productivity. However, this approach comes with trade-offs, often sacrificing:

  • Simplicity
  • Consistency
  • Performance

img.png A classic overview of monolith architecture vs microservices

What if you could start a project with the benefits of a monolith and well-defined domain boundaries, then evolve into microservices when the time is right? This is the promise of modular monolith architecture.

What is a modular monolith?

A modular monolith, also known as a modulith, is a software architecture style where the application is developed and deployed as a monolith but is internally divided into distinct, loosely coupled modules.

Each module has well-defined boundaries and responsibilities, allowing for separation of concerns, easier maintenance, and independent development within the monolith.

These modules could later evolve into full-blown microservices.

img.png Overview of modular monolith architecture

Advantages of starting with a modular monolith

Modular monolith architecture provides some key benefits over microservices:

Simplicity in Development Starting with a modular monolith means developing a single codebase that can be tested comprehensively. This simplicity can significantly streamline the initial stages of development.

Well-Defined Yet Flexible Boundaries Modular monoliths, like microservices, emphasize well-defined boundaries between modules. However, the single codebase offers greater flexibility in evolving these boundaries as teams gain deeper insights into their domain.

Enhanced Performance and Consistency In the early stages of a project, business concerns often take precedence over technical concerns like performance optimization or handling eventual consistency. A modular monolith allows you to focus on business needs first, addressing technical challenges as the project matures.

Flexibility in Module Evolution One of the key advantages of a modular monolith is its flexibility. Unlike microservices, where boundaries are rigid, a modular monolith allows for easier refactoring of module boundaries as your understanding of the domain evolves. This flexibility extends to managing dependencies between modules, making it easier to add or remove functionality as needed.

How to build a modular monolith?

  1. Define Clear Module Boundaries
    Start by identifying and defining the core domains and functionalities of your application. Each module should
    encapsulate a distinct area of responsibility. Use domain-driven design (DDD) principles to ensure that these
    boundaries align with the business logic.
  2. Implement Strong Encapsulation
    Ensure that each module has well-defined interfaces and internal implementation details that are hidden from other
    modules. This helps maintain separation of concerns and minimizes the risk of unintended interactions between
    modules.
  3. Separate Data at the Schema Level
    Distribute database tables across modules, with each module having its own schema. This practice strengthens module
    boundaries and prepares for an easier transition to microservices by maintaining data isolation.
  4. Maintain a Single Codebase
    Keep all modules within a single codebase initially. This simplifies development, testing, and deployment processes.
  5. Treat Each Module as an Independent Application
    Organize your source code so that each module is a separate folder representing a domain. Within each module, use the
    same architecture you would for an independent application, such as layered architecture or ports and adapters.
    Tip: It’s normal to have some shared code between modules. Try to limit shared code to infrastructure logic and avoid
    sharing business logic.
  6. Test Between Edges of Modules
    Test each module independently and mock interactions with other modules. This simplifies your testing efforts.

When to choose a modular monolith in new projects?

When starting a new project, a modular monolith can be an excellent choice, depending on several factors:

Uncertain Domain Boundaries

If your domain boundaries are not yet fully defined, a modular monolith offers the flexibility to adjust as your understanding grows.

Team Capacity

A smaller or less experienced team may benefit from the simplicity and reduced overhead of managing a single codebase.

Gradual Scaling

If your immediate scaling needs are modest, a modular monolith allows you to focus on delivering business value without the complexity of a microservices architecture.

When it is time to move into microservices?

A modular monolith architecture can evolve into microservices when the time is right. Here are some factors that can lead in that direction.

Reaching Technical Limitations

A modular monolith is, at the end, a monolith, and some factors such as scalability and deployment time can only be optimized up to a certain point. When these limitations start to affect growth, it may be time to consider microservices.

Heterogeneous Requirements

As the application evolves, different parts may require distinct technical foundations, such as different databases optimized for specific tasks or programming languages suited for high-performance operations. Microservices are more suitable for these diverse needs.

Growing Team Size A single codebase can become unmanageable as team size increases. Beyond a certain threshold, the benefits of individual services developed by independent teams outweigh the simplicity of a single codebase.

Conclusion

Choosing the right architecture is critical for the long-term success of any software project. Starting with a modular monolith can provide a balanced approach, leveraging the simplicity and consistency of a monolithic architecture while maintaining the flexibility to transition to microservices when the time is right. This strategy allows for a smoother evolution, accommodating growing complexity, scaling needs, and diverse technical requirements over time. By understanding the principles and advantages of a modular monolith, teams can effectively navigate the initial development stages with confidence and clarity, ensuring robust and maintainable solutions that can evolve with the business.

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts