Why I Chose a Modular Monolith Instead of Microservices
Architectural decisions shape long-term maintainability. After evaluating both patterns, I chose a modular monolith over microservices for this project. This post explains the reasoning, module boundaries, transaction management, deployment differences, and the trade-offs that led to this decision.
Architecture Options
A modular monolith organizes the codebase into distinct modules with clear boundaries, all running within a single process and deployment. Microservices split the system into independently deployable services communicating over the network. The choice impacts data consistency, deployment complexity, and team organization.
Module Boundaries
The monolith is divided into modules by domain: core, billing, reporting, and auth. Each module owns its tables and business logic. Module boundaries are enforced through package-level access control and well-defined interfaces. This provides the organization benefits of microservices without the operational overhead.
Transactions & Data Consistency
< Spring Boot's single-process nature means ACID transactions are straightforward. The Java Persistence API (JPA) manages transaction boundaries with @Transactional. In a microservices architecture, distributed transactions require two-phase commit or eventual consistency patterns, which add latency and complexity. The modular monolith avoids these trade-offs entirely.Deployment
A single JAR artifact contains the entire application. Deployment is a simple java -jar command or a Docker image with one layer. Microservices require CI/CD pipelines for each service, service discovery, load balancing, and orchestration (Kubernetes or similar). The monolith reduces deployment time from minutes to seconds.
Comparison & Trade-offs
- Operational complexity: Monolith wins. One process to monitor, one set of logs, one database connection pool.
- Team autonomy: Microservices allow independent team ownership, but this project's small team benefited from shared context in a monolith.
- Scaling: Microservices allow per-service scaling, but the monolith scales vertically well and can be horizontally scaled behind a load balancer if needed.
- Data consistency: Monolith offers strong consistency with ease; microservices require saga patterns or eventual consistency.
Microservices might become justified if the team grows beyond 15 engineers, if different domains require completely different tech stacks, or if independent scaling of a specific bounded context is critical.