SaaS & data, an interactive architecture walkthrough

Multi-tenant SaaS analytics pipeline: how the architecture works

How a SaaS product collects events from many customers, keeps each tenant's data isolated, and serves fast dashboards from a columnar store.

This is a learning explainer based on public engineering writing and general design practice. It is simplified, it is not the actual architecture of any company, and it may be inaccurate.

27 components, 29 connections, 13 steps. Updated 27 October 2026.

Interactive diagram

Overview

A multi-tenant analytics pipeline serves many customers from shared infrastructure, so the tenant id is carried through every stage. Events are authenticated and rate-limited at ingestion, spread across a stream keyed by tenant and user, enriched, and written to a columnar store where the query layer injects the tenant filter itself. A control plane tracks plans, keys and usage for billing.

Trade-offs and what changes at scale

Shared tables with a tenant_id column are cheap to run and fast to onboard, but isolation depends on every query being scoped; separate databases or clusters per tenant isolate better and cost far more, so large or regulated tenants are often moved to dedicated resources. Keying the stream by tenant alone would send a large tenant's whole load to one partition, so keys include a user or session id and the biggest tenants can get dedicated topics. Putting tenant_id first in the ClickHouse sort key keeps tenant scans cheap. Usage is counted asynchronously, so quotas are enforced approximately in real time and billed exactly later. At scale the work shifts to per-tenant quotas, retention by plan, and deleting one tenant's data on request, which means partitioning ClickHouse by tenant or accepting slow mutations, and deciding how the immutable Parquet archive is erased.

How it works, step by step

The walkthrough above, as text. Each step lights up the components named under it.

  1. An end-user action in the tenant's app is captured by the SDK, which batches events and attaches the tenant's write key before sending. Write keys are public in browsers, so abuse is limited by origin checks and a per-key rate limit.

    Components Analytics SDK, End UsersVia app events

  2. The gateway terminates TLS and routes the batch to the collector, shielding it from malformed or oversized traffic.

    Components API Gateway, Collector ServiceVia HTTPS batch

  3. The collector resolves the write key to a tenant from a short-lived cache of the control plane's data in Redis (refreshed or invalidated by the control plane when keys change), going to the control plane only on a miss so ingestion doesn't depend on it for every batch. It then applies per-tenant rate limits and quotas from Redis. Bad keys or exhausted quotas are rejected early.

    Components Collector Service, Control Plane API, Rate Limit StoreVia forward

  4. Events are validated against the schema registry so events that fail validation are rejected before the stream and cannot poison downstream consumers. Free-form properties are validated loosely.

    Components Schema RegistryVia validate

  5. Valid events are published to Kafka keyed by tenant and user or session id. That spreads one large tenant's load across partitions while each user's events stay in order, and the stream is a durable buffer if downstream slows.

    Components Event StreamVia publish event

  6. The stream processor enriches events with geo/IP and user agent, deduplicates and sessionises them. Keying by session keeps one user's state on one partition, so sessionisation stays correct.

    Components Stream ProcessorVia consume

  7. The stream processor writes processed events to ClickHouse in large batches (ClickHouse wants few big inserts, not many tiny ones), with tenant_id on every row; late or duplicate events are handled by deduplication within a time window. Raw copies are archived as Parquet in S3 for replay and reprocessing, and the archive can be read back into the stream processor.

    Components Analytics Store, Raw Event ArchiveVia insert

  8. A tenant analyst opens the dashboard and signs in; the identity provider issues a JWT carrying the tenant id.

    Components Dashboard Web App, Tenant Analyst, Identity ProviderVia opens

  9. The query API verifies the JWT locally with the identity provider's cached signing keys and injects the tenant filter itself, so clients do not choose the tenant. This holds because the API accepts structured queries, not raw SQL. ClickHouse row policies or per-tenant views can enforce the same filter in the database as a second line of defence.

    Components Query API, Identity ProviderVia HTTPS + JWT

  10. The tenant-keyed Redis cache is checked first; on a miss the scoped query runs on ClickHouse and the result is cached, protecting the store from repeated dashboards.

    Components Query Cache, Analytics StoreVia cache check

  11. The collector counts usage in memory and Redis, never with a Postgres write per request. Redis counters are volatile, so they drive quota enforcement and an early estimate; the billable figure is reconciled against deduplicated event counts in ClickHouse. The billing job reads those counters on a schedule, aggregates them per tenant against plans, writes totals to Postgres, and reconciles against those ClickHouse counts before invoicing. Quota enforcement uses the same counters.

    Components Metering & Billing Job, Analytics Store, Rate Limit Store, Control DBVia aggregate usage

  12. A tenant admin onboards through the control plane, which creates the tenant, plan and write keys in its Postgres; keys and quotas then reach the collector through the Redis cache. Noisy-neighbour limits keep one tenant from starving others.

    Components Control Plane API, Control DB, Collector ServiceVia onboarding, keys

  13. Every tier emits metrics to observability, with per-tenant detail for the top tenants (or in logs and ClickHouse), since per-tenant metric labels explode cardinality, to spot lag, hot partitions and slow queries.

    Components Observability, Stream Processor, Event StreamVia metrics

Sequence diagram

The same walkthrough as a UML sequence diagram, one lane per layer or group (20 components in all), left to right in the order a request reaches them, numbered by step. A loop on a lane is a step that stays inside that group. Solid arrows are synchronous calls, dashed are asynchronous.

Multi-tenant SaaS analytics pipeline: sequence diagram of the walkthroughTenant AppsIngestion EdgeControl PlaneStreaming &ProcessingData LayerQuery &DashboardObservabilityapp events1HTTPS batch2forward2forward3key lookup (miss)3limits, usage counts3invalidate / refresh3validate4publish event5consume6insert7Parquet archive7replay7opens8login8HTTPS + JWT9fetch signing keys9cache check10scoped SQL10aggregate usage11event counts11read usage counters11onboarding, keys12SQL12invalidate / refresh12metrics13metrics13metrics13metrics13metrics13

Key components

Tenant Appsboundary

  • Analytics SDK: JS/mobile SDK, batches events (javascript)
  • End Users: Customers' app users
  • Tenant Admin: onboarding and API keys
  • Tenant Analyst: Views dashboards

Ingestion Edgelayer

  • API Gateway: TLS, routing, L7 limits
  • Collector Service: Auth write key (cached), quotas, validate
  • Rate Limit Store: Token buckets, usage counters, cached keys (redis)
  • Schema Registry: Event schemas per tenant

Control Planedomain

  • Control DB: Tenants, plans, keys, usage (postgresql)
  • Control Plane API: Tenants, plans, API keys
  • Identity Provider: Issues JWT with tenant id
  • Metering & Billing Job: Aggregates usage per tenant

Streaming & Processinglayer

  • Event Stream: Keyed by tenant + user/session id (kafka)
  • Stream Processor: Enrich, dedupe, sessionise (flink)

Data Layerlayer

  • Analytics Store: tenant_id on every table (clickhouse)
  • Raw Event Archive: Data lake, Parquet

Query & Dashboardlayer

  • Dashboard Web App: Charts and reports
  • Query API: JWT auth, tenant-scoped queries
  • Query Cache: Tenant-keyed results (redis)

Observability

  • Observability: Metrics, logs, traces

Sources

Public writing this explainer draws on. Read these for what the real systems actually do.

Make it yours

Copy this diagram and its walkthrough into your own workspace, then change anything: swap Postgres for DynamoDB, add a cache, rewrite the narration. Free, and it uses no AI credits.

This is a learning explainer based on public engineering writing and general design practice. It is simplified, it is not the actual architecture of any company, and it may be inaccurate. Real systems differ in the details. Browse the library.