โ† Back to Portfolio.NET ยท EF Core ยท Multi-tenant

SaasDemo

A multi-tenant SaaS platform where a central tenant manager routes requests to independent Pharmacy and Laboratory services, each backed by its own database โ€” with the database engine itself swappable per tenant at runtime.

Problem

Built at Danphe Health, solving an actual onboarding problem: bringing a new hospital client onto the system meant a lot of repeated per-client setup, when the direction the business wanted โ€” an internal effort called Danphe VNext โ€” was everything running in the cloud behind a single shared API serving every client.

Context

Multi-tenancy sounds simple until a client's data actually has to stay isolated from every other client's, some clients need to run on a cheaper database engine than others for licensing reasons, and none of that can require rewriting the system every time a new client signs on.

Architecture

SaasDemo mirrors that setup: a central SAAS project acts as the tenant manager, routing requests to independent Pharmacy and Laboratory services based on a tenant header, each tenant backed by its own database. Every app follows onion architecture โ€” Core, Infrastructure, Web API โ€” so the actual business logic doesn't depend on infrastructure details like which database engine sits behind it.

Key decisions

  • Onion architecture, influenced by Robert C. Martin ("Uncle Bob") and Jason Taylor's Clean Architecture work โ€” the goal being a system that accepts new requirements without breaking what's already there.
  • Runtime-switchable database provider (PostgreSQL or SQL Server) via config, with a separate EF Core migration project per provider โ€” driven by a real constraint, not a technical exercise: SQL Server licensing gets expensive, and clients needed a cheaper option available.
  • Per-tenant database isolation, resolved dynamically per request from the authenticated user's claims, rather than any static per-client configuration.

Trade-offs

Supporting two database providers means maintaining two parallel sets of EF Core migrations โ€” more to keep in sync, in exchange for real licensing flexibility for clients. Onion architecture adds structural overhead too โ€” more projects, more indirection โ€” that only pays for itself when requirements actually keep changing, which at Danphe VNext, they did.

The hardest part

Resolving the correct tenant's database purely from the authenticated user's claims โ€” routing every request to the right connection without ever leaking data across tenants.

Lessons learned

Connection strings for each tenant lived in the master database. It worked, but looking back, that's exactly the kind of secret that belongs in a proper key vault, not a database row โ€” that's the first thing I'd change if I rebuilt this today.

Technology

.NET, C#, EF Core, PostgreSQL, SQL Server, Onion Architecture

View on GitHub โ†’