One platform, many storefronts: multi-tenancy by department
A money-services operator is rarely a single shop. Here is how MEXAR uses the department as its tenancy boundary so branches, brands, and franchisees run isolated — yet roll up into one view.
Very few licensed operators run a single counter. There are branches, a head office, sometimes franchisees or distributor partners — each with its own cash, its own rates, its own staff, and its own regulatory footprint. MEXAR models that reality with one primary boundary: the department.
The department is the tenancy line
A department represents a store or business unit, and it is the line almost all data is scoped to. Transactions, customer entities, configured currencies, exchange rates, stock accounts, and the per-entity financial config (fees, limits, commissions) all carry a department. One branch cannot see or touch another branch’s books, because the data was never shared in the first place.
That isolation is not a permission filter bolted on top — it is structural. Scope lives in the data model, so “show me this branch’s transactions” and “this branch’s stock” are the natural shape of every query, not a rule someone has to remember to apply.
Isolated, but not islands
Isolation alone would make group reporting impossible, so departments form a
hierarchy. Every department records both its direct parent
(parent_department_id) and the top-level ancestor (root_department_id):
Root Department (head office)
├── Branch A
│ ├── Counter A1
│ └── Counter A2
└── Branch B
The hierarchy is what powers aggregated reporting and cross-department visibility: a head office can roll up the numbers beneath it, while each counter still operates in its own lane. One deployment serves the whole group — not one instance per shop, with the reconciliation headache that implies.
Accounting in each department’s own currency
Money businesses span currencies, but a branch still thinks in one — the
currency it reports profit in. So every department carries a base_currency_id,
and all “to-base” calculations convert into it. A Jakarta branch can roll
everything into IDR while a Singapore branch rolls into SGD, on the same
platform, each with a coherent P&L in the denomination it actually books in.
Currencies and rates are themselves per-department (DepartmentCurrency,
DepartmentCurrencyRate), and rates can differ by customer group — so a
branch can quote a walk-in customer and a partner differently without spinning up
a separate system.
People belong to departments — with a role
Staff are scoped too, but flexibly: a single user can be a member of multiple departments, with a different role in each. Department membership carries one of a small set of roles that describe the kind of access a person has there:
| Department role | Access |
|---|---|
ROOT | full access, system-wide |
MANAGER | full access to the department’s resources |
EMPLOYEE | read/write, gated by fine-grained permissions |
ANALYST | read-only |
CUSTOMER | limited to their own user-level resources |
This department role is deliberately separate from the fine-grained permission system: the two are evaluated together. To act on a resource, a user must be a member of the department with an appropriate role and hold the specific permission. Membership answers “where”, permissions answer “what”.
Why operators care
The multi-tenant department model is what lets MEXAR scale with a business instead of fragmenting it:
- Clean isolation — each branch’s transactions, stock, and customers are separated by construction, which is exactly what an auditor wants to see.
- Group reporting — the hierarchy rolls branches up to head office without merging their books.
- Per-department economics — each unit prices, stocks, and reports in its own base currency and rate set.
- One deployment — add a branch by adding a department, not by standing up another system.
For an operator opening their fifth location — or onboarding a franchisee — that is the difference between growth and a migration project. The same boundary also underpins how the platform controls access and how it stays extensible by plugin.