Modular Monoliths: Why 42% of Teams Are Leaving Microservices Behind
The microservices revolution promised teams independence, scalability, and faster deployments. A decade later, many organizations are discovering that the operational overhead, infrastructure costs, and coordination complexity far outweigh the benefits — especially for teams that never needed planet-scale architecture in the first place.
According to a 2025 CNCF survey, 42% of organizations that adopted microservices are now consolidating services back into larger deployable units. The destination? A pattern that's been quietly gaining momentum: the modular monolith.
What Is a Modular Monolith?
A modular monolith is a single deployable application organized into well-defined modules based on business domains. Each module encapsulates specific functionality and interacts with others through explicit interfaces — but everything runs in a single process.
Think of it as microservices boundaries without the network calls:
┌──────────────────────────────────────────┐
│ Single Deployment │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Orders │ │ Payments │ │ Users │ │
│ │ Module │ │ Module │ │ Module │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ In-Process APIs │ │
│ ┌────┴────────────┴──────────┴─────┐ │
│ │ Shared Infrastructure │ │
│ └───────────────────────────────────┘ │
└──────────────────────────────────────────┘
You get clear module boundaries, domain-driven design, and independent development — without service meshes, distributed tracing, or cross-service debugging nightmares.
Why Teams Are Making the Switch
1. The Cost Reality
Microservices infrastructure costs are 3.75x to 6x higher than monoliths for equivalent functionality. When you factor in infrastructure, operations, platform teams, and coordination overhead:
- Monolith: ~$15,000/month
- Microservices: $40,000–$65,000/month
Amazon Prime Video's team demonstrated this dramatically — they migrated their Video Quality Analysis service from distributed microservices to a monolith and achieved a 90% infrastructure cost reduction.
2. The Complexity Tax
Every network call between microservices introduces latency, failure modes, and debugging complexity. Teams spend more time managing infrastructure than building features:
- Service discovery and load balancing
- Distributed transaction management
- Cross-service authentication
- API versioning across dozens of services
- Coordinated deployments and rollbacks
With a modular monolith, module-to-module calls are simple function calls — no serialization, no network hops, no retries.
3. The Team Size Mismatch
Microservices architecture was designed by companies like Netflix and Google — organizations with thousands of engineers. Most companies have 10–50 developers. For these teams, microservices create organizational overhead without matching benefits.
A modular monolith lets a 10-person team move fast with clear ownership boundaries, without needing a dedicated platform engineering team.
Modular Monolith in Practice
Project Structure
Here's how a modular monolith looks in a typical Node.js/TypeScript project:
src/
├── modules/
│ ├── orders/
│ │ ├── api/ # Public module API
│ │ │ └── index.ts # Exported interfaces
│ │ ├── domain/ # Business logic
│ │ ├── infrastructure/# DB, external services
│ │ └── tests/
│ ├── payments/
│ │ ├── api/
│ │ ├── domain/
│ │ ├── infrastructure/
│ │ └── tests/
│ └── users/
│ ├── api/
│ ├── domain/
│ ├── infrastructure/
│ └── tests/
├── shared/ # Cross-cutting concerns
│ ├── events/
│ ├── database/
│ └── auth/
└── app.ts # Composition root
Enforcing Module Boundaries
The key discipline is preventing modules from reaching into each other's internals. In TypeScript, you can enforce this with path aliases and lint rules:
// tsconfig.json
{
"compilerOptions": {
"paths": {
"@orders/*": ["src/modules/orders/api/*"],
"@payments/*": ["src/modules/payments/api/*"],
"@users/*": ["src/modules/users/api/*"]
}
}
}// ✅ Correct: importing from module's public API
import { createOrder } from '@orders';
// ❌ Wrong: reaching into module internals
import { OrderRepository } from '../orders/infrastructure/repository';Event-Driven Communication
Modules communicate through an in-process event bus rather than direct calls. This keeps modules decoupled while avoiding network overhead:
// shared/events/event-bus.ts
type EventHandler = (payload: unknown) => Promise<void>;
class EventBus {
private handlers = new Map<string, EventHandler[]>();
on(event: string, handler: EventHandler) {
const existing = this.handlers.get(event) || [];
this.handlers.set(event, [...existing, handler]);
}
async emit(event: string, payload: unknown) {
const handlers = this.handlers.get(event) || [];
await Promise.all(handlers.map(h => h(payload)));
}
}
export const eventBus = new EventBus();// modules/orders/domain/order-service.ts
import { eventBus } from '@shared/events';
export async function completeOrder(orderId: string) {
const order = await markOrderComplete(orderId);
await eventBus.emit('order.completed', {
orderId: order.id,
amount: order.total,
userId: order.userId,
});
}// modules/payments/domain/payment-listener.ts
import { eventBus } from '@shared/events';
eventBus.on('order.completed', async (payload) => {
const { orderId, amount, userId } = payload as OrderCompletedEvent;
await processPayment(userId, amount, orderId);
});Framework Support in 2026
The ecosystem has matured significantly:
- Spring Modulith (Java/Kotlin): First-class support for modular monoliths with built-in module verification, event publication, and documentation generation.
- ABP Framework (.NET): Full modular monolith toolkit with domain-driven design patterns baked in.
- Nest.js (Node.js): Module system naturally supports this pattern with dependency injection and clear module boundaries.
- Django (Python): App-based architecture already follows modular monolith principles.
When to Choose What
| Factor | Modular Monolith | Microservices |
|---|---|---|
| Team size | 5–100 engineers | 100+ engineers |
| Scale | Thousands to millions of users | Billions of requests |
| DevOps maturity | Small ops team | Dedicated platform team |
| Priority | Speed and cost efficiency | Independent scaling |
| Deployment | Single artifact | Per-service pipelines |
Start with a modular monolith. If a specific module genuinely needs independent scaling, extract it into a service. This is far easier than combining scattered microservices into a coherent system.
The Path Forward
The modular monolith isn't a step backward — it's a pragmatic response to years of over-engineering. It delivers 80% of microservices benefits (modularity, clear boundaries, domain-driven design) at 20% of the cost (no service mesh, no distributed tracing, no cross-service debugging).
For SMEs and startups in the MENA region and beyond, this architecture is especially compelling. You get clean, maintainable code with clear team ownership — without the six-figure infrastructure bills that come with microservices done right.
The industry is learning a lesson that experienced architects have known all along: the best architecture is the simplest one that solves your actual problems.
Discuss Your Project with Us
We're here to help with your web development needs. Schedule a call to discuss your project and how we can assist you.
Let's find the best solutions for your needs.