Why We Chose a Monolithic Architecture Over Microservices for ArchGuard
The team behind the open‑source ArchGuard platform recounts their migration from Maven to Gradle, library updates, and a surprising shift from a microservice‑based system to a monolithic architecture, explaining the challenges of legacy services, the principles guiding service granularity, and the practical benefits of a single‑code‑base deployment for a small open‑source project.
During this month the ArchGuard team began open‑sourcing their internal architecture‑governance platform and performed a series of legacy‑system migrations, including moving from Maven to Gradle for its flexible custom tasks and incremental builds, updating dependency libraries, and re‑evaluating the overall system structure.
Challenges of the Legacy Microservice System
After internal discussions the team identified several pain points:
Too many services/modules, resulting in a sprawling codebase.
Insufficient documentation, making it hard to understand past technical decisions without digging through Git history.
Complex deployment pipelines compared with simpler tools like Jenkins or SonarQube.
Service boundaries that did not align with actual scaling needs (e.g., only the scanner required elastic scaling).
These issues prompted a reconsideration of service granularity, referencing classic microservice guidelines such as Conway's Law, the two‑pizza team rule, high cohesion/low coupling, and cost‑benefit analysis.
Why a Monolith Fits the Current Context
Inspired by Martin Fowler’s "Monolithic First" essay, the team concluded that a monolithic deployment is more suitable for an early‑stage open‑source project with limited resources. Key reasons include:
Docker‑based deployment is straightforward and affordable for a SaaS‑like offering.
The primary users are developers who also act as contributors, so a single‑click start improves the developer experience.
Open‑source projects such as Gradle and Spring traditionally favor monolithic builds.
Faster initial setup and deployment times.
Target Architecture: Monolith with Modular Design
The team adopted a Domain‑Driven Design (DDD) layered architecture, organizing each resource, aggregate, or service into its own package (except for shared common). The repository structure resembles:
├── Application.kt
├── clazz
├── code
├── common
├── config
├── evaluation_bak
├── evolution
├── method
├── metrics
├── module
├── packages
├── qualitygate
├── report
├── report_bak
├── scanner
├── scanner2
└── system_infoFiles ending with _bak or located in scanner2 are remnants from the migration process.
Migration Steps
Merge multiple repositories while preserving commit history using git-filer-repo for path filtering.
Consolidate build configurations (e.g., application.properties) across modules.
Standardize dependency versions to avoid conflicts.
Resolve code conflicts such as duplicate Application classes, bean definitions, and service implementations.
The migration itself is straightforward but time‑consuming.
Alternative Approaches
For teams managing many microservices without a need for single‑machine deployment, a monorepo strategy—placing all service code in one repository—can be advantageous, as demonstrated by large organizations like Google.
Open Questions
While a monolith works well for small teams, the article raises the question of scalability for larger teams and how to prevent the monolith from becoming an unmanageable “big ball of mud.”
phodal
A prolific open-source contributor who constantly starts new projects. Passionate about sharing software development insights to help developers improve their KPIs. Currently active in IDEs, graphics engines, and compiler technologies.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
