When should a .NET application use a modular monolith instead of immediately adopting a microservices architecture?
Question
Answers
Microservices are often presented as the natural architecture for large applications, but distributed architecture introduces substantial complexity.
A microservices application may require:
- Service-to-service communication
- API contracts
- Distributed tracing
- Message brokers
- Independent deployments
- Service discovery
- Distributed configuration
- Resilience patterns
- Network security
- Data ownership
- Distributed transactions
- Monitoring
- Operational automation
Therefore, the decision should not be:
“Is the application large enough for microservices?”
Instead, architects should ask:
“Do we have a business and operational reason to distribute the system?”
A modular monolith can provide strong internal boundaries while maintaining a simpler deployment model.
What is a modular monolith?
Imagine an ASP.NET Core application containing:
Sales Inventory Customer Management Billing Notifications Reporting
These modules can have:
- clear responsibilities
- separate domain logic
- controlled dependencies
- internal interfaces
- separate application services
- independent tests
- defined data ownership
But the application can still be deployed as a single application.
This creates a useful middle ground between:
Poorly structured monolith
and
Highly distributed microservices architecture
Example
Consider an HR application.
Modules could include:
Employee Leave Payroll Benefits Recruitment
If the organisation has one development team, a common deployment cycle, moderate traffic, and no requirement for independent scaling, introducing five microservices may create more operational complexity than business value.
A modular monolith can establish boundaries first.
Later, if Payroll genuinely requires independent scaling or deployment, it may become a candidate for extraction.
Important architectural signals
Microservices may become more appropriate when there is a strong requirement for:
Independent deployment
A business capability needs to be released without deploying the entire application.
Independent scaling
One component experiences significantly different workload characteristics.
Team autonomy
Different teams independently own different business capabilities.
Technology independence
A particular service has a legitimate requirement for a different technology stack.
Failure isolation
One capability needs to fail independently without bringing down unrelated functionality.
Clear bounded contexts
The business domains have sufficiently strong boundaries to justify independent services.
Database ownership
One of the most important considerations is data ownership.
A common mistake is creating microservices while allowing every service to directly access the same database tables.
That creates:
Microservice A ─┐ Microservice B ─┼── Shared Database Microservice C ─┘
The system may look like microservices externally but remains tightly coupled internally.
A more independent architecture establishes ownership boundaries.
Practical recommendation for students
Students should first learn how to build a well-structured modular .NET application before attempting microservices.
They should understand:
- Dependency Injection
- Clean architecture principles
- Domain boundaries
- Application services
- Repository/data-access boundaries
- API design
- Messaging
- Authentication
- Logging
- Testing
- Observability
Then they can understand why and when those boundaries might need to become independently deployable services.
Expert point
Microservices should be treated as an architectural and organisational decision, not simply a technology choice.
A well-designed modular monolith can be a deliberate architecture—not an outdated architecture—and can provide a practical path toward future decomposition when genuine business or operational requirements emerge.