Systems · From July 2026

Architecture for a multi-tenant lead-intelligence SaaS

Wrote the requirements, architecture and detailed design for a multi-tenant lead SaaS, and built a demo for client review.

The problem

What was needed, and what made it hard.

A client wanted a SaaS that ingests public-records filings, enriches each case with owner contact data from third-party contact-data APIs, validates quality, and pushes sales-ready leads into each customer's CRM. Many customers share one deployment. Enrichment costs money per lookup, so each lookup had to be billable to the tenant that asked. The filings themselves are public and identical for everyone. The client's requirements arrived in rounds, so the design had to stay stable while three decisions stayed open. I owned the specification and the demo, so the two had to agree.

My role: Sole owner: architect, requirements owner, specification author and builder of the frontend demos.

The system

The specified path of one lead, in order. This is design: I built only the demo screens, not this pipeline.

Filing ingest 1 Shared store 2 Enrichment 3 Tenant database 4 CRM delivery 5 Control plane 6 Filing ingest 1 Shared store 2 Enrichment 3 Tenant database 4 CRM delivery 5 Control plane 6
The specified path of one lead, in order. This is design: I built only the demo screens, not this pipeline.

Select a component to read what it does and how it fails.

  1. Filing ingest. Polls public-records sources once for everyone, which keeps it cheap. If polling stalls, every tenant's filings go stale together.
  2. Shared store. One database holds property, owner, filing and enrichment results once. A bad write here reaches every tenant.
  3. Enrichment. Contact-data lookups run only when a tenant asks, and are billed to that tenant. A provider outage delays requests, not ingestion.
  4. Tenant database. Everything a tenant owns lives in its own database. Isolation is structural, not a query filter; the price is many migrations.
  5. CRM delivery. Validated, sales-ready leads are pushed into the customer's CRM. As the last hop, its failures are what the customer sees first.
  6. Control plane. A separate repo, backend and privilege boundary for provisioning, tenant resolution and migrations. It reaches every tenant, so guard it.

Decisions

What I chose, what I turned down, and what it cost.

1Share the public data, isolate the paid data

I chose
One shared source-of-truth database holds property, owner, filing and enrichment results once. Everything a tenant owns sits in a database per tenant.
I turned down
Copying the public data into every tenant's database.
Why
Filings are identical for everyone, so wide, cheap ingestion should happen once. Enrichment is expensive and demand-scoped, so it stays billed to the tenant that asked.
What it cost
Two data planes to keep consistent, and a bad write to the shared store reaches every tenant.

2Split the control plane

I chose
A separate repo, backend and privilege boundary for the admin plane, with provisioning, tenant resolution, migration execution and shared-infrastructure placement written down.
I turned down
Building it beside the tenant backend as extra routes.
Why
The code that can create, migrate and inspect every tenant should not share a deploy or a credential set with the code every tenant's users reach.
What it cost
Two backends to run, and a written contract between them that has to stay true.

3Trace every requirement to a screen

I chose
Requirement IDs run from the requirements document into the demo, and the demo shows only what the document specifies.
I turned down
A richer demo with screens the document did not ask for.
Why
A reviewer can walk from any requirement to the screen that shows it, so disagreement becomes a change to the specification, not an argument about a mock-up.
What it cost
The demo could not reveal what the requirements missed. That took the client's written input, which left three decisions open for me to close.

What broke

The honest boundary.

Nothing broke, because nothing ran in production. The demo is a review artifact: it shows the specified screens, not a working pipeline. When I last checked, the production backend had not been started, so I have no incident to report and will not invent one.

Outcome

What came of it, and what it was built with.

  • I finished the requirements (v1.3), architecture (v1.3) and detailed design (v1.1) documents, after a discovery pass on market, competitors, data sources, workflows and personas.
  • I closed the three decisions the client's second-round input left open: a dual-mode data-pool toggle, dismissed-date handling and a filing schema placeholder.
  • This took the architecture and demo from nothing to something a client could review. It did not take a product to launch.

Built with

  • Next.js
  • React
  • Tailwind CSS
  • Docker
  • Traefik

Specified, not built by me

  • NestJS
  • Python
  • FastAPI
  • Celery
  • PostgreSQL
  • OpenSearch
  • Redis
  • Auth0
  • Terraform

The rule I took from this

The line I keep from this project.

Write the specification first, and trace every requirement to a screen.

Rule 7 of 10

Keep reading