ADR-001: Modular Monolith Architecture
Status
Accepted
Context
We are building a medical prescription management platform (Prescriptoo) with an initial user base of approximately 1,000 doctors. The system needs to support multiple bounded contexts including identity, organizations, patients, prescriptions, subscriptions, and admin functionality.
We needed to decide between:
- Full Microservices: Separate deployable services from day one
- Modular Monolith: Single deployable with clear module boundaries
- Monolith: Single deployable without module separation
Decision
We will use a Modular Monolith architecture with clear module boundaries, event-driven communication, and Go workspaces for dependency isolation.
Rationale
Why Not Full Microservices?
| Factor | Our Situation | Microservices Threshold |
|---|---|---|
| Scale | ~1000 doctors (10-50 concurrent) | Need 1M+ req/day or 50+ devs |
| Team | 1-3 developers | Need 5+ teams for independent deploys |
| Complexity | Early stage, many stubs | Each service adds operational overhead |
| Debugging | Cross-service tracing needed | Hard without mature observability |
Why Modular Monolith?
- Single deploy, single log stream, single database transaction - Simpler operations
- Module boundaries preserved - Can extract to microservices later when needed
- Go workspaces - Each module has its own
go.modfor dependency isolation - Event-driven communication - Modules communicate via events, making extraction mechanical
- No network latency - In-process calls for critical paths
Future Extraction Path
When we hit 50+ concurrent users or need independent deploys:
- Extract auth service (most independent)
- Add API gateway (Traefik)
- Keep other modules in monolith
Consequences
Positive
- Simpler deployment and operations
- Easier debugging with single log stream
- Module boundaries preserved for future extraction
- Go workspaces provide dependency isolation
Negative
- Cannot scale modules independently
- Database is shared (mitigated by table prefixes)
- Risk of tight coupling if boundaries not enforced
Related Decisions
ADR-002: Table Prefix over Separate Schemas
ADR-003: Event-Driven Architecture with Swappable Bus