ido-crm
A CRM with several teeth — accounting, sales pipeline, security staffing, and an ATIS complaint system. Each its own module, sharing one identity and audit layer. Backend integration owned end-to-end.
Overview
ido-crm is the back office of a security firm dressed as a SaaS. Four real businesses run inside it: accounting, sales pipeline, security-staffing, and an ATIS complaint workflow. Each is a module; each writes to the same identity, the same audit log, and the same Postgres schema partitioned by tenant.
I owned backend integration for fourteen months at BufferSol on a team of six. The brief was "make four products feel like one product." The product manager had a list of 200 things; we shipped them in 7 modules.
The Problem
Multi-tenancy is the obvious technical challenge. Less obvious — and the one that quietly burns teams — is **module independence**. Each product team wanted to ship on their own cadence. None of them wanted to coordinate releases. All of them wanted the same login.
Solution: hard module boundaries with soft data sharing. Identity, RBAC, and audit are the only cross-module concerns. Everything else lives behind a versioned internal API.
The Approach
NestJS modules with a strict import rule: a module may import from `@ido/identity` and `@ido/audit` and nothing else from a sibling. Cross-module reads go through a small `@ido/internal-api` package that lives in the same repo but is treated as if it were external — versioned, deprecated on a schedule, never broken silently.
Postgres schema-per-tenant for hard isolation. RBAC at the row level; every query that crosses a tenant boundary requires a system role explicitly held by maintenance jobs, never by the API. Audit log is append-only, partitioned by month, and the only thing legal asked us to add later — and we already had it.
Deploys are GitHub Actions → Docker → Kubernetes. The boring choice. Twelve weeks in we had it under nine minutes. Twenty-six weeks in we had it under five.
Stack Deep-Dive
Seven modules, one identity, one audit log. That ratio is the project. Everything else is implementation detail.
Results
Operator throughput up 60% measured by tickets-closed-per-shift across the staffing module — the kind of number the customer cared about, not the kind we like to put on dashboards. Deploy time down 50% from the inherited pipeline. Seven modules in production, each independently deployable, each behind a feature flag we actually used.
Most useful artefact: a module-boundary lint rule we built in week three that blocks cross-module imports at PR time. We never had a single illegal cross-module import merged in the entire fourteen months, because the build refused to.