Architecture
Overview
A modular monolith, sized to be operated without an ops team.
The architecture answers three constraints: accounting rigour, an unstable network, and an operator who is not an IT specialist. Every component we do not add is an outage you will not have.
End-to-end architecture
AiO · end-to-end architecture
< 3s on 4G · 99,5% · 2 000 concurrent users
AiO · modular monolith · live traffic
One deployable unit · module boundaries inside
Docker · GitHub Actions (CI/CD, security scan) · PostgreSQL WAL + point-in-time recovery. One unit to deploy, one to operate — no message bus, no orchestrator. Dots are requests in flight.
Why a modular monolith, not micro-services
This is a deliberate choice, and it runs against what you will be offered elsewhere.
Your need is measured in tens of operations per minute, not tens of thousands per second. A properly tuned PostgreSQL handles 10,000 to 30,000 transactions per second: the database will never be your bottleneck. Micro-services, on the other hand, would force a message bus, orchestration, distributed monitoring and cross-service tracing on you — that is three times the operating cost, for capacity you have no use for.
So we keep the module boundaries — they structure the code and the Git repository — but one deployment to operate. The day a module has to be extracted, the boundary already exists.
Six layers, one deployment
The ledger is the centre
Everything else derives from it. Balances, trial balance, reports, accounting and regulatory statements are not separate tables to keep in sync: they are reads of the journal. That is what makes the classic gap between “what the screen says” and “what accounting says” impossible.
Ready to be resold
Your §3.2 creates a publisher profile responsible for managing licences; your chapter 25 asks for multi-company administration. Module boundaries and isolation are therefore drawn with that future in mind: a second company is onboarded without a rewrite, and rate cards, thresholds, charts of accounts, currencies and roles are data, not code.
Version 1 is deployed for one legal entity. Isolation is delivered and usable; support for opening a second entity is quoted separately.