Systems
The code for most of this is private; it belongs to the businesses that run on it. The design isn't. Each diagram below is a real system in production; click any node to see what it does and why it's there.
Multi-tenant SaaS
How one request can't reach another shop's data
Every request in the retail ERP/POS SaaS passes four checkpoints before it touches a row. Click a node to see what it does and why it exists.
Access token
Tenant identity lives in the token, nowhere else
The organization a caller belongs to is carried in the signed access token. Controllers never read organizationId from a request body, a header or a query string. Those are all under the client's control. If you can't forge the token, you can't claim another shop.
// Controllers derive tenant from AuthContext, never from the request.
export interface AuthContext {
userId: string;
organizationId: string;
membershipId: string;
role: Role;
branchId: string;
allowedBranchIds: string[];
canAccessAllBranches: boolean;
terminalId: string | null; // set when a paired till fixes the branch
permissions: Set<Permission>;
}Wholesale garments ERP
How the books always balance
The ERP's numbers are exact by construction: integer money, lot-level costing, and a ledger you can only append to. Click through the model.
Money
A third decimal you can't lose
Omani Rial has three decimal places: 1 rial is 1000 baisa. The Money value object holds a bigint count of baisa, so addition and multiplication by quantity are exact. Rates (VAT, discounts) go through Decimal.js and round half-up back to baisa explicitly. It persists to NUMERIC(18,3). No float ever touches a monetary figure.
export class Money {
private readonly baisa: bigint;
static fromOmr(value: string | number | Decimal): Money {
const dec = new Decimal(value).mul(1000)
.toDecimalPlaces(0, Decimal.ROUND_HALF_UP);
return new Money(BigInt(dec.toFixed(0)));
}
multiplyByQty(qty: number): Money {
if (!Number.isInteger(qty)) throw new Error(`Quantity must be an integer`);
return new Money(this.baisa * BigInt(qty));
}
}Building something with the same shape?
If your product has tenants, money, or both, the decisions above are the ones that matter. I'm happy to talk through yours before anything is built.