Engineering Notes
ERP platform architecture for real-world operations
A practical view of ERP architecture based on Businext: a modular monolith built with FastAPI where each business area runs as an independent mounted module within the same backend platform.
Architecture Guide
2026-03-09
10 min read
The real problem of ERP architecture
ERP architecture discussions often start with technologies. Frameworks, databases or deployment models.
In practice the real problem is how to allow multiple business domains to coexist in the same platform without letting modules absorb rules they do not own.
In real enterprise systems each domain pushes architecture in different directions.
Finance needs traceability and reliable closing.
Inventory needs strict movement rules.
Sales needs speed and customer visibility.
Human resources needs permission and organizational context.
ERP architecture must support all of them while remaining understandable as the system grows.
That is why ERP architecture is mainly about boundaries, contracts and domain ownership.
Starting from the operational model
Before choosing services or infrastructure, the platform must answer a more fundamental question:
Which operational flows keep the business running?
Typical ERP flows include:
- recording financial operations correctly
- coordinating sales and operational fulfillment
- tracking inventory movements with auditability
- supporting administrative and HR workflows
- exposing consistent reporting across domains
When architecture starts from tables instead of operational flows, the backend often becomes easy to scaffold but hard to evolve.
Decomposition by business domains
In Businext the system was decomposed around business domains.
Each business area operates as a module with clear responsibilities:
CRM
HRM
SRM
PMS
EMS
RMS
IMS
SMS
AMS
EIS
Each module runs as a FastAPI application mounted within the same backend.
Each domain maintains its own engineering structure:
- SQLAlchemy models
- Pydantic schemas
- API routers
- domain services
This approach allows each domain to evolve without spreading changes across the entire platform.
Architecture designed for evolution
Many internal systems work well in the early stages even with weak boundaries. Problems appear when:
- features grow
- operational exceptions increase
- more engineers interact with the system
- the business becomes dependent on the platform
At that moment architecture quality is measured by how safe it is to change the system.
A practical ERP architecture emphasizes:
- explicit domain ownership
- service layers modeling business flows
- data consistency over implementation convenience
- visibility into operational state transitions
Why the modular monolith worked
For many ERP platforms adopting microservices too early increases operational complexity before the domain model stabilizes.
Businext was built as a modular monolith.
This allowed:
- a simple deployment boundary
- direct integration between domains
- explicit internal contracts
- lower operational complexity for a single developer
- fast system evolution while the model matured
A modular monolith only works when domain boundaries are real. Without clear boundaries it only centralizes coupling.
Where good architecture pays off
Solid ERP architecture produces benefits in three areas.
Change isolation
When finance rules change, other modules should not reinterpret them. They should consume the capability exposed by the domain that owns the rule.
Operational support
The system must make it possible to understand what happened, why it happened, and which state transition produced the result.
Trust in data
Reports should reflect truth owned by each domain.
A practical rule
A useful ERP design rule is simple:
Every operationally important workflow should have a domain that owns the decision and a visible path for executing it.
If ownership is unclear, the workflow is probably relying on accidental coupling.
Businext was not designed as microservices, but as a modular monolith where each business domain evolves inside a FastAPI application mounted within the same backend.