Modularity isn’t microservices: How modular monoliths redefine scalable software development

12/22/2024
Fabala Dibbasey

Modularity isn’t Microservices: Rethinking software architecture

Microservices have become synonymous with modern software architecture. They structure applications into smaller, independent services that can be developed, tested, and deployed autonomously. While microservices enable modularity, they are not the only way to achieve it—and they are not without their costs and risks.

When thinking about modularity, many developers assume microservices are the go-to solution. But microservices can be expensive to implement and maintain, requiring significant investment in infrastructure and expertise. Worse, improperly executed microservices can lead to the dreaded distributed monolith—a tightly coupled system of services that becomes a nightmare to scale and maintain.

The trap of the distributed monolith

A distributed monolith arises when developers split a monolithic application into multiple interdependent services without addressing the underlying coupling. The result is a fragmented system where changes to one service cascade across others, creating a “big ball of mud” no one wants to touch.

To avoid this trap, it’s worth considering whether microservices are necessary at the outset. For many new projects, a simpler and more scalable approach may lie in the Modular Monolith.

What is a modular monolith?

A modular monolith organizes an application into independent modules with well-defined boundaries that are loosely coupled but deployed as a single unit. Unlike microservices, these modules are logical rather than physical, reducing the overhead of managing distributed systems.

Key benefits of modular monoliths:

  • Loose coupling: Encourages separation of concerns without the complexity of distributed services.

  • Scalability: Can evolve into microservices if needed, allowing for future growth.

  • Cost-effectiveness: Avoids the infrastructure costs and operational overhead of microservices.

  • Simplified communication: Enables in-memory communication, avoiding external message brokers like RabbitMQ or Kafka.

An example: Building a modular monolith for a mobile money app

Let’s imagine a Mobile Money application with the following core functionalities:

  • Wallet module: Handles peer-to-peer transfers, deposits, and withdrawals through agents.

    • Architecture: N-Tier. A straightforward choice for a foundational module.

  • Payment module: Supports bill payments, airtime purchases, and online transactions.

    • Architecture: Vertical Slice. Ideal for feature-intensive modules, ensuring new features don’t disrupt the system.

  • User management module: Manages profiles, notifications, authentication, and authorization.

    • Architecture: Clean Architecture. Allows for technology flexibility, such as switching from Cloudinary to AWS S3 without disrupting the module.

  • Interoperability Module: Facilitates communication between mobile money providers.

    • Architecture: MVC (Yes, it’s still a thing!).

By assigning distinct architectural patterns to each module based on its requirements, you can create a system that balances simplicity, scalability, and maintainability.

Module communication the right way

Communication between modules in a modular monolith can be synchronous or asynchronous. However, bringing microservices overhead into module communication (e.g., with RabbitMQ or Kafka) defeats the purpose of simplicity.

Instead, an in-memory message bus like MediatR can implement publish/subscribe patterns for lightweight, asynchronous communication. This approach avoids the complexity of managing external systems while maintaining scalability.

Conclusion: Modularity without the microservice overhead

Microservices are a powerful tool, but they are not a one-size-fits-all solution. For many projects, starting with a Modular Monolith provides the flexibility, cost-effectiveness, and scalability needed to succeed—without falling into the distributed monolith trap.

By leveraging architectural patterns like Vertical Slice and Clean Architecture, modular monoliths can achieve the best of both worlds: modularity and simplicity. When scalability or availability demands arise, these systems can evolve into microservices, ensuring long-term adaptability.

Modularity isn’t microservices—it’s about choosing the right architecture for your goals.

Insights

Our Recent Articles

Let's build your high-performance team—fast and hassle-free!

Schedule a consultation