Event-Driven Notification Platform · View 03 of 26 · 2 · Structure
Layered Architecture
What depends on what, and the single dependency that points the wrong way.
Copy
PNG
PDF
⋯
Editable source
SVG
draw.io
All views
Experience
Experience
Admin Console
React
Admin Console...
Recipient Inbox
Centrifugo WebSocket
Recipient Inbox...
Producer SDKs
Go · Java · Python
Producer SDKs...
API
API
Event API
POST /v1/events
Event API...
Notification API
direct send
Notification API...
Preference API
Preference API
Template API
Template API
Admin API
replay · config
Admin API...
Decision
Decision
Rules Engine
CEL over event + context
Rules Engine...
Orchestrator
multi-step · fallback
Orchestrator...
Preference Service
Preference Service
Scheduler
timers · cron · digests
Scheduler...
Composition
Composition
Template Service
draft · published · rollback
Template Service...
Render Service
Render Service
Recipient Profile
subject_ref to contact
Recipient Profile...
Localisation
locale · timezone
Localisation...
Delivery
Delivery
Channel Router
Channel Router
Channel Adapter SPI
one contract per channel
Channel Adapter SPI...
Delivery Workers
6 channel pools
Delivery Workers...
Provider Clients
2 per channel
Provider Clients...
Messaging
Messaging
Apache Kafka
Strimzi · KRaft
Apache Kafka...
Schema Registry
Apicurio · Avro
Schema Registry...
DLQ Topics
per tier · 30 d
DLQ Topics...
Data
Data
PostgreSQL
notifications · config
PostgreSQL...
ClickHouse
delivery attempts
ClickHouse...
Redis
dedup · counters
Redis...
MinIO and Iceberg
archive · audit
MinIO and Iceberg...
Platform
Platform
Kubernetes
Kubernetes
Istio ambient
mTLS everywhere
Istio ambient...
HashiCorp Vault
tenant credentials
HashiCorp Vault...
Keycloak
OIDC
Keycloak...
Grafana LGTM
observability
Grafana LGTM...
REST + OIDC
REST + OIDC
produce acks=all
produce acks=all
notification intent
notification intent
render request
render request
composed message
composed message
writes
writes
cached preferences
cached preferences
upward call
upward call
Layered Architecture
Layered Architecture
Application we own
Application we own
Interface / broker
Interface / broker
Queue / topic
Queue / topic
Security / platform
Security / platform
Data store
Data store
synchronous
synchronous
event / async
event / async
failure / alternate
failure / alternate
Dependencies point downward with one exception, drawn in red: the in-app worker pushes into the experience layer. Accepted because the alternative is client polling at 750 M notifications a day.
Dependencies point downward with one exception, drawn in red: the in-app worker pushes into the experience layer. Accepted because the alternative is client polling at 750 M notifications a day.
v 1.0 · owner Data & AI Global Practice · date 2026-08
v 1.0 · owner Data & AI Global Practice · date 2026-08
Text is not SVG - cannot display
The layering rule
A layer may call the layer below it and may publish upward only through the messaging layer
The decision layer never calls a delivery worker directly; it publishes an intent
Producers depend on the API layer alone and know nothing about channels, templates or providers
The exception, drawn in red
The in-app worker pushes into the experience layer over WebSocket, which is an upward call
The alternative is client polling at 750 M notifications a day, which costs more than the violation
Contained by making Centrifugo the only upward target and treating it as infrastructure, not application code
Why open source throughout
Every layer has a mature Apache-2 or MIT option with an operator, so there is no managed-service lock-in
The two places this costs effort are Kafka and Flink operations, budgeted as two platform engineers
Channel providers are the one unavoidable exception — FCM and APNs have no self-hosted equivalent
◀ High-Level Architecture
All views
Container and Component Architecture ▶