In active build
A heavy-civil and mining contractor needed one system for people, procurement, stock and project accounting, with each site running its own operation without fragmenting the data.
01Context
Contracting operations are distributed by nature. Sites are remote, staffing moves between them, materials are ordered locally but accounted for centrally, and each site may be a separate legal entity for tax purposes. Most SMB ERP products model a single company at a single location; the workarounds for anything else are where the data integrity goes.
02What was going wrong
03Approach
A two-level tenant → organisation model. One client holds many sites or legal entities under a single tenant, with the boundary enforced in the database rather than assumed in the application. Client-specific rules are configuration and extension on a shared codebase, never a fork — the discipline that decides whether a platform survives its fifth customer.
A deliberate position. Queries are written, read and tuned explicitly, so performance is predictable and the data access layer holds no hidden behaviour. It costs more up front and pays back the first time something is slow.
Every table carries the tenant boundary. An application bug cannot leak across tenants because the application is not the thing enforcing it.
Client-specific rules configure on the common codebase. Clients keep receiving platform improvements instead of being stranded on a private version.
Change history is a system property recorded from the start, not a feature added when somebody eventually asks for it.
04What was built
05Outcome
In active build with the first client onboarded as tenant one. Modules are being taken into live use in slices rather than held for a single release. No performance or savings figures are quoted because the system has not been in production long enough to produce defensible ones.
We will walk through the architecture, the constraints and the parts that went wrong — on a call, without a deck.