ERP System
Modular monolith architecture for production-grade enterprise workflows.
Architecture
Modular monolith
Database model
Schema isolation
Reliability
Transactional events
The system needed to support growing ERP domains without turning every module into an independent service. The main challenge was keeping boundaries clear while preserving deployment and operational simplicity.
A traditional layered monolith would make feature delivery fast at first, but domain boundaries would become harder to enforce as modules started sharing tables, services, and implicit assumptions.
HTTP/API Layer
Receives requests and delegates use cases without owning domain logic.
Module Boundary
Each module exposes explicit interfaces and hides persistence details.
PostgreSQL Schemas
Separate schemas make ownership and accidental coupling easier to control.
Outbox Worker
Dispatches domain events reliably after the transaction commits.
- Model each business capability as an explicit module with its own application contracts.
- Use PostgreSQL schema-per-module isolation so database ownership is visible and enforceable.
- Keep module communication in-process, but only through interfaces rather than direct database access.
- Publish cross-module events through an outbox table written in the same transaction as business data.
Choose modular monolith before microservices
The system needed strong boundaries, not distributed systems overhead. A single deployable kept operations simpler while still allowing modules to evolve independently.
Use schema-per-module instead of shared public tables
Database ownership is one of the easiest boundaries to accidentally break. Separate schemas make ownership visible during development and review.
Use outbox for cross-module events
Publishing events outside the database transaction risks losing messages. The outbox pattern keeps state changes and event intent consistent.
Less runtime isolation than microservices
A module bug can still affect the single process, but the tradeoff keeps deployment, tracing, and local development far simpler.
More discipline required in code review
The architecture only works if boundaries are actively maintained. Interface contracts and schema ownership need to be reviewed consistently.
- Boundaries should be visible in code and in the database, not only in diagrams.
- A monolith can stay maintainable if module ownership is explicit from the beginning.
- Reliable event publishing is a data consistency problem before it is an infrastructure problem.