Tenancy Architecture

Multi-tenancy, single-tenancy, update strategies, isolation models, and the evolution path for scaling the Clarity platform to hundreds of customers.

cloud_done

Current Architecture

Clarity operates a single-tenant, dedicated-instance model. Each customer receives their own Kubernetes namespace ({{acronym}}-{{env}}), database instance, Docker container set (backend, frontend, Redis sidecar), Client layer with customer-specific Startup.cs and overrides, plugin configuration, NFS storage mount, SSL certificate, and subdomain.

The codebase is shared via Docker image tags — all customers run the same backend and frontend images. Customer-specific behavior is driven by:

  1. The Client layer compiled at build time
  2. Database configuration and settings
  3. Kubernetes secrets for credentials

Deployment Unit per Customer

deployed_code

K8s Namespace: acme-prod

terminal Backend Container (.NET 8)
web Frontend Container (Remix)
memory Redis Sidecar
storage Dedicated Database (SQL Server)
folder NFS Storage Mount
lock SSL Certificate + Subdomain
compare

Tenancy Model Comparison

Dimension Single-Tenant Current Multi-Tenant Shared DB Multi-Tenant DB-per-Tenant Recommended
Deployment Unit 1 K8s namespace + DB per customer 1 shared app + 1 shared DB 1 shared app + 1 DB per tenant
Data Isolation COMPLETE — separate everything ROW-LEVEL — TenantId filter required DATABASE-LEVEL — strong isolation
PCI Compliance STRONGEST — fully isolated PCI environment WEAKEST — filter bug could leak tokens STRONG — separate databases
Performance Isolation COMPLETE — no noisy neighbor NONE without throttling PARTIAL — DB isolated, app shared
Update Speed N operations (one per customer) Single deployment, instant Single deployment + per-tenant migrations
Customization Full (Client layer + hooks + plugin) Config + feature flags only Config + feature flags only
Cost at Scale Highest (dedicated compute per customer) Lowest (shared everything) Moderate (shared app, dedicated DBs)
Provisioning ~5 minutes (infra + config) ~30 seconds (DB + registry) ~30 seconds (DB + registry)
tune

Customization Without Forking

settings Layer 1 — Configuration (80%+ of customizations)

The Settings system stores customer-specific preferences: payment provider selection, ERP connector, feature toggles, notification rules, surcharge configuration, and branding/theme. All managed through the admin UI without any code changes.

conversion_path Layer 2 — Pipeline Hooks (code, non-invasive)

The Client layer registers pre-hooks and post-hooks on any pipeline. Example: a customer needs custom surcharge calculation based on their state's rules — they add a hook on CalculateSurchargePipeline that adjusts the result after default logic runs. Core is untouched. Other customers unaffected.

extension Layer 3 — Client Plugin (code, fully isolated)

For truly unique requirements, a ClientPlugin is registered last in Startup.cs. It can override any previously registered service, add new entities, create API endpoints, and inject frontend routes. Because it's registered last, it wins any DI conflicts. Because it's in the Client layer, it ships only with that customer's deployment.

info

The key insight: Client customizations are ADDITIVE. They hook into pipelines and override services — they never modify Core or Plugin source code. When Core or Plugins are updated via submodule pointers, customizations continue to work as long as pipeline interface contracts are maintained.

trending_up

Path to Multi-Tenancy

1 Fleet Management

  • check_circle Provisioning automation scripts
  • check_circle Central health dashboard across all instances
  • check_circle Fleet-wide image update pipeline (canary → staged → full)
  • check_circle Centralized logging and monitoring

Immediate ROI, zero architectural risk

2 Shared Application Tier

  • check_circle Tenant resolution middleware (hostname → TenantId → connection string)
  • check_circle Scoped DbContext resolved per-request from tenant registry
  • check_circle Database-per-tenant with central tenant registry (Redis-cached)
  • check_circle Migration runner iterates all tenant databases on deploy
  • check_circle Per-tenant rate limiting and resource quotas

3 Full Platform

  • check_circle Tenant Admin Portal for self-service provisioning
  • check_circle Tiered plans (Enterprise / Standard / Self-Service)
  • check_circle Usage metering and billing integration
  • check_circle Template-based onboarding (e.g., “NetSuite + Standard Order-to-Cash”)
  • check_circle Marketplace for connector and plugin distribution
layers

Hybrid Model

apartment Tier 1 — Enterprise

  • check Single-tenant, dedicated instance
  • check Full customization (Client layer + hooks + ClientPlugin)
  • check Own K8s namespace, version pinning
  • check Controls own update schedule

Example: Fortune 500 running D365 F&O with 20 custom pipeline hooks

business Tier 2 — Standard

  • check Multi-tenant, DB-per-tenant
  • check Config + feature flags + standard hook patterns
  • check Shared application tier, isolated database
  • check Receives updates on Clarity's release schedule

Example: Mid-market distributor on NetSuite with standard order-to-cash

storefront Tier 3 — Self-Service

  • check Multi-tenant, shared/pooled infrastructure
  • check Configuration-only customization
  • check Fastest provisioning, lowest cost

Example: Small business using basic invoice payments

info

The same Docker image serves all tiers. The difference is deployment topology, not codebase.

policy

Data Isolation & Compliance

PCI DSS Compliance by Model

STRONGEST Single-Tenant PCI — each customer is a fully isolated PCI environment
STRONG DB-per-Tenant PCI — payment tokens in separate databases, SAQ-A maintained
UNACCEPTABLE Shared DB PCI — SQL injection could expose all tenants' tokens

SOC 2 Considerations

  • check_circle Logical access controls enforced at connection string level
  • check_circle Audit trails maintained per-tenant with no cross-contamination
  • check_circle Change management tracked per deployment unit
  • check_circle Encryption at rest and in transit for all tenant data

Data Residency

  • check_circle Single-tenant: database can be provisioned in any cloud region
  • check_circle DB-per-tenant: individual databases can be placed in required regions
  • check_circle Supports GDPR, CCPA, and industry-specific residency requirements

Breach Blast Radius

  • shield Single-tenant: breach affects only one customer's data and infrastructure
  • shield DB-per-tenant: application compromise could affect multiple tenants, but database breach is limited to one tenant
  • warning Shared DB: any database breach exposes ALL tenants' data
info

For payments platforms, shared-database multi-tenancy is not recommended. Database-per-tenant provides strong isolation while enabling the operational benefits of a shared application tier.

security

Defense-in-Depth Isolation

1

Connection String Isolation

Application physically cannot access another tenant's DB. Connection string resolved from tenant registry per authenticated request.

2

Tenant Context in Pipeline

Every IPipelineContext includes resolved TenantId. Cross-cutting operations require elevated permissions and are audited.

3

EF Core Global Query Filter

Belt-and-suspenders: modelBuilder.Entity<BaseEntity>().HasQueryFilter(e => e.TenantId == _currentTenantId) prevents cross-tenant data even if wrong connection string resolved.

4

JWT Tenant Claim

Authenticated user's JWT contains TenantId claim. Middleware validates token tenant matches resolved tenant. Mismatch → 403 Forbidden.

5

Integration Tests

Tests provision 2+ tenant databases and verify operations in Tenant A's context NEVER return Tenant B's data.

6

Runtime Monitoring

Flags any database query returning data with TenantId different from request's tenant context.

person_add

Customer Onboarding

Today (Single-Tenant)

8 Steps

Infrastructure (~5 min)

check_circle 1. Create K8s namespace
check_circle 2. Provision database
check_circle 3. Create K8s secrets
check_circle 4. Generate deployment YAML
check_circle 5. Deploy via kubectl

Business Config (30-60 min)

check_circle 6. Site Setup Wizard
check_circle 7. Configure connector
check_circle 8. Configure payment provider

Multi-Tenant (DB-per-Tenant)

4 Steps

Infrastructure (~30 sec)

check_circle 1. Create database + run migrations + register tenant

Business Config (30-60 min)

check_circle 2. Site Setup Wizard
check_circle 3. Configure connector
check_circle 4. Configure payment provider

Self-Service Vision

Tenant Admin Portal: click “Create Customer” → DB provisioned → migrations run → tenant registered → customer receives login link. Pre-loaded templates for common configurations.

system_update

Update & Rollback Strategy

Scenario Single-Tenant Multi-Tenant (DB-per-Tenant)
Security patch Fleet pipeline pushes new image to all namespaces. Staggered rollout possible. Single deployment restart. ALL tenants patched immediately.
Feature release New image + migration per customer. Can be staggered across customers. Single restart + migration runner iterates tenant DBs. Feature flags for gradual enable.
Breaking connector change Only affected customers updated independently. Feature flag gates new behavior per tenant.
Rollback Roll back individual customer's image tag. Others unaffected. Roll back entire application. All tenants revert. Can roll back individual tenant DBs separately.
Customer requests delay Pin their deployment to specific commit hash. Feature flags disable new behavior for that tenant.
Emergency hotfix Deploy patched image to ONLY their namespace. Zero risk to others. Deploy to shared app — all tenants receive it. OR promote to single-tenant temporarily.
info

This is why the hybrid model is recommended: customers with regulatory constraints or code freeze requirements can use single-tenant (Tier 1) for full version control, while standard customers benefit from instant multi-tenant updates.

help

FAQ

Every customer receives their own dedicated database instance — your data is physically separate from every other customer, not just logically partitioned within a shared database. On top of that, the platform enforces six layers of isolation: dedicated connection strings per tenant, scoped pipeline context, EF Core global query filters as a safety net, JWT-based tenant claim validation on every request, automated integration tests that verify cross-tenant data cannot leak, and runtime monitoring that alerts on any tenant context mismatch. In today’s single-tenant deployment, each customer also runs in separate application containers, making cross-tenant access physically impossible. Learn more about the defense-in-depth isolation model and our data handling practices.

You stay in full control of your update timeline. In the current single-tenant model, updates are deployed to each customer independently — your environment can be on a different version than any other customer, and deployments can be scheduled around your business calendar, peak seasons, or internal change-management windows. In a multi-tenant deployment, the application is updated once for all tenants, but feature flags provide granular control so new capabilities can be enabled or disabled per customer. Either way, you’re never surprised by unexpected changes. See the full update & rollback strategy.

Absolutely. In a single-tenant deployment, your environment is pinned to a specific version — other customers can move forward while yours stays frozen for as long as needed. When you’re ready, the update is applied and any pending database migrations run automatically. In a multi-tenant deployment, feature flags keep new behavior disabled for your tenant while others receive it. For critical regulatory freezes (like quarter-close or audit periods), a tenant can even be temporarily promoted to a dedicated single-tenant instance for complete version control. See the update & rollback strategy for details.

Today’s infrastructure provisioning is fully automated and completes in approximately 5 minutes — that includes creating the Kubernetes namespace, database, application containers, SSL certificate, and DNS entry. From there, business configuration (connecting your ERP, setting up payment providers, and configuring site settings) typically takes 30–60 minutes in collaboration with your team. As the platform evolves toward multi-tenancy, infrastructure provisioning drops to roughly 30 seconds since only a new database and tenant registry entry are needed. The long-term vision includes a self-service tenant admin portal for near-instant provisioning. See the full onboarding walkthrough.

Clarity uses a three-layer customization model that keeps the core platform unified. Layer 1 — Configuration handles over 80% of customization needs through settings, feature toggles, and branding — no code required. Layer 2 — Pipeline Hooks lets you inject custom logic at any point in a business workflow (before payment capture, after order sync, etc.) without modifying the core engine. Layer 3 — Client Plugin provides a dedicated code project for truly unique requirements like custom entities, service overrides, or additional API endpoints. All three layers are additive and isolated, so core platform updates apply cleanly without breaking any customizations. Learn more about customization layers and pipeline hooks.

We recommend a hybrid model that matches the right deployment tier to each customer’s needs. Enterprise customers with complex customizations, strict compliance requirements, or regulatory obligations receive dedicated single-tenant instances with full version control and isolation. Standard mid-market customers benefit from efficient multi-tenant deployment with database-per-tenant isolation — delivering faster provisioning and lower overhead while maintaining strong data separation. The same platform, codebase, and engineering team serves both tiers seamlessly, with no forks and no divergence. Explore the hybrid tier model for the full breakdown.

Performance isolation is built into every layer. At the database level, each tenant has their own dedicated database instance, so a large data sync or heavy reporting query from one customer never touches another’s resources. At the application tier, per-tenant rate limiting, concurrency caps on ERP connector sync operations, background task quotas, and Kubernetes horizontal pod autoscaling ensure that no single tenant can monopolize shared compute resources. Enterprise customers on dedicated instances have zero noisy-neighbor risk by design. The result: consistent, predictable performance for every customer regardless of what others are doing on the platform.

All connector credentials — ERP API keys, payment gateway tokens, OAuth secrets — are stored exclusively in each customer’s own dedicated database within encrypted Settings entities. There is no shared or centralized credential store. When the platform needs to authenticate to your ERP or payment provider, it resolves the tenant-scoped database context, reads your credentials from your database only, and establishes the connection. Credentials never appear in Docker images, source control, environment variables, or any shared configuration. This means even in a multi-tenant deployment, one customer’s credentials are physically inaccessible to another customer’s application context.

Each customer’s database has independent backup schedules with point-in-time recovery — typically a 15-minute RPO (Recovery Point Objective) using cloud-managed database services. The application tier is fully stateless, which means it recovers instantly via Kubernetes pod rescheduling with no data loss. Redis cache is ephemeral by design — if lost, it causes a brief performance dip while the cache warms, but no business data is affected. In multi-tenant deployments, the tenant registry (the routing layer that maps requests to databases) is the highest-priority component and is replicated with the strongest durability guarantees. For organizations requiring geographic redundancy, cross-region disaster recovery is supported through cloud-managed database replication and multi-region Kubernetes deployment.

Yes — full white-labeling is built into the platform. The theme editor in the Remix frontend allows per-tenant customization of colors, fonts, logos, and layout through Tailwind CSS variables stored in the database. Embedded payment forms automatically inherit the tenant’s theme so they feel native within ERP interfaces. Email notifications and customer-facing communications use per-tenant Handlebars templates with your branding. The result is a seamless chain: your team sees your brand in the admin portal, and your end-customers see your brand on payment pages, invoices, and email receipts — with no trace of the underlying platform.