Core Concepts
Read time: 10 minutes
The Three-Layer Architecture
Prism separates what you want from how it works and where data lives.
Layer 1: Client API (What)
The stable interface your application uses. Five core APIs:
KeyValue → Store and retrieve by key
PubSub → Publish events, subscribe to topics
Queue → Send and receive ordered messages
Reader → Read-only access to data streams
Transact → Multi-operation transactions
Key point: These APIs never change, even when backends or patterns change.
Layer 2: Patterns (How)
Reliability patterns Prism applies automatically:
WAL (Write-Ahead Log) → Durability guarantees
Outbox → Transactional messaging
Claim Check → Handle large messages
CDC (Change Data) → Database change capture
Tiered Storage → Hot/cold data management
Key point: You request guarantees (durability: strong), Prism selects patterns.
Layer 3: Backends (Where)
Data storage backends:
Kafka → High-throughput event streaming
PostgreSQL → Transactional data
Redis → Fast key-value and pub/sub
NATS → Lightweight messaging
Key point: Platform team controls backend selection and provisioning.
Example: How Layers Work Together
Your request:
client_api: queue
needs:
durability: strong
retention: 30days
write_rps: 5000
What Prism does:
Layer 1 (What): Expose Queue API
↓
Layer 2 (How): Apply WAL pattern for durability
↓
Layer 3 (Where): Use Kafka + PostgreSQL
Result:
- You call
queue.send(message)(Layer 1) - Prism writes to WAL on disk, then Kafka (Layer 2)
- Data stored in Kafka topic + PostgreSQL WAL table (Layer 3)
- You never see Layers 2-3—handled transparently
Namespaces
A namespace is an isolated data access scope.
Namespace Properties
namespace: order-processing # Unique identifier
team: payments # Owning team
client_api: queue # API type
Namespace Isolation
Each namespace provides:
- Separate data: No cross-namespace visibility
- Independent config: Different durability, retention per namespace
- Access control: Per-namespace authorization
- Resource allocation: Dedicated backend resources
Example:
orders-service namespace:
- client_api: queue
- backend: Kafka topic "orders-service-queue"
- access: payments team only
analytics namespace:
- client_api: pubsub
- backend: NATS subject "analytics.events"
- access: analytics team + data-science team
Data Models
Prism provides 10 data models composed of 45 thin interfaces.
KeyValue Model
Store and retrieve data by key.
Interfaces:
keyvalue_basic: Get, Set, Deletekeyvalue_scan: Scan keys by patternkeyvalue_ttl: Automatic expirationkeyvalue_transactional: Multi-key operationskeyvalue_batch: Bulk operationskeyvalue_cas: Compare-and-swap
Backends: Redis, PostgreSQL, DynamoDB, MemStore
Use cases:
- Session storage
- Configuration management
- Caching
- Feature flags
PubSub Model
Publish events to topics, subscribe to receive events.
Interfaces:
pubsub_basic: Publish, Subscribepubsub_wildcards: Pattern subscriptions (e.g.,user.*)pubsub_persistent: Durable subscriptionspubsub_filtering: Server-side event filteringpubsub_ordering: Ordered delivery guarantees
Backends: NATS, Redis, Kafka
Use cases:
- Event notifications
- Real-time updates
- Microservice communication
- Activity feeds
Queue Model
Ordered message delivery with acknowledgment.
Interfaces:
queue_basic: Send, Receive, Acknowledgequeue_visibility: Visibility timeout for retriesqueue_dead_letter: Failed message handlingqueue_priority: Priority-based deliveryqueue_delayed: Scheduled message delivery
Backends: PostgreSQL, SQS, RabbitMQ
Use cases:
- Background job processing
- Task queues
- Order processing
- Workflow orchestration
Stream Model
Append-only log of events with replay capability.
Interfaces:
stream_basic: Append, Readstream_consumer_groups: Parallel consumptionstream_replay: Read from any pointstream_retention: Automatic old data cleanupstream_partitioning: Ordered parallelism
Backends: Kafka, Redis Streams, NATS JetStream
Use cases:
- Event sourcing
- Audit logs
- Change data capture
- Metrics collection
TimeSeries Model
Time-ordered data with aggregation.
Interfaces:
timeseries_basic: Append, Query by time rangetimeseries_aggregation: Downsampling, rollupstimeseries_retention: Automatic data expirationtimeseries_interpolation: Fill missing data points
Backends: ClickHouse, TimescaleDB, InfluxDB
Use cases:
- Metrics and monitoring
- Sensor data
- Financial data
- Application performance monitoring
Backend Capabilities
Backends implement thin interfaces instead of exposing all features.
Example: Redis Capabilities
Redis implements 16 interfaces across 5 models:
KeyValue Model:
✓ keyvalue_basic (Get, Set, Delete)
✓ keyvalue_scan (Scan, Count)
✓ keyvalue_ttl (Expire, GetTTL)
✓ keyvalue_batch (MGet, MSet)
PubSub Model:
✓ pubsub_basic (Publish, Subscribe)
✓ pubsub_wildcards (Pattern matching)
Stream Model:
✓ stream_basic (XADD, XREAD)
✓ stream_consumer_groups (XGROUP)
Example: Kafka Capabilities
Kafka implements 8 interfaces focused on streaming:
Stream Model:
✓ stream_basic (Produce, Consume)
✓ stream_consumer_groups (Consumer groups)
✓ stream_replay (Seek to offset)
✓ stream_retention (Topic retention)
✓ stream_partitioning (Topic partitions)
PubSub Model:
✓ pubsub_basic (Topics as pub/sub)
✓ pubsub_ordering (Partition ordering)
✓ pubsub_persistent (Durable storage)
Why interfaces matter:
- Type-safe: Compiler enforces contracts
- Composable: Multiple backends can fulfill pattern needs
- Explicit: No hidden capabilities or surprises
Configuration Ownership
Client-Controlled Configuration
Application teams control what they need:
# You specify:
client_api: pubsub
needs:
write_rps: 1000 # Capacity estimate
retention: 7days # Data lifetime
durability: strong # Reliability level
max_message_size: 100KB # Message size limit
Platform-Controlled Configuration
Platform team controls how and where:
# Platform configures (you never see this):
patterns:
- wal: {fsync: true}
- claim_check: {threshold: 50KB}
backend:
type: kafka
partitions: 20
replication: 3
Why this separation?
- Prevents misconfiguration: Clients can't select incompatible patterns
- Enables evolution: Platform changes implementation without breaking apps
- Enforces best practices: Platform ensures correct pattern composition
Authorization Boundaries
Platform team defines policy boundaries to guide client choices.
Guided Mode (Default)
Pre-approved backends for all teams:
allowed_backends:
- postgres # For queues, transactions
- kafka # For streams, pub/sub
- redis # For caching, key-value
- nats # For lightweight messaging
Result: Teams self-service within safe boundaries.
Advanced Mode (Approval Required)
Backend-specific tuning needs platform approval:
# Requires platform team review:
needs:
kafka_partitions: 50 # Custom partition count
postgres_pool_size: 100 # Custom connection pool
Expert Mode (Platform Team Only)
Unrestricted access for platform team:
# Platform engineers only:
backend:
type: custom-redis-cluster
connection: "redis://custom-endpoint"
replication_factor: 5
PII Handling
Prism automatically handles Personally Identifiable Information (PII) based on protobuf annotations.
Annotating PII
message UserProfile {
string user_id = 1;
string email = 2 [
(prism.pii) = "email",
(prism.encrypt_at_rest) = true,
(prism.mask_in_logs) = true
];
string ssn = 3 [
(prism.pii) = "ssn",
(prism.encrypt_at_rest) = true,
(prism.mask_in_logs) = true,
(prism.audit_access) = true
];
}
Automatic PII Handling
Prism generates code that:
- Encrypts at rest: Data encrypted before storage
- Masks in logs: PII redacted from observability
- Audits access: All PII reads logged for compliance
- Validates schemas: Ensures PII annotations are enforced
Example log output:
INFO: Processing UserProfile user_id=123 email=***@***.*** ssn=***-**-****
Observability
Every namespace includes automatic observability.
Built-In Metrics
prism_requests_total{namespace="orders", api="queue", status="success"}
prism_latency_seconds{namespace="orders", api="queue", percentile="p99"}
prism_backend_errors_total{namespace="orders", backend="kafka"}
Built-In Tracing
Distributed traces span all layers:
Trace: order-processing-request
├─ queue.send() [1.2ms]
│ ├─ wal.write() [0.3ms]
│ └─ kafka.produce() [0.9ms]
└─ response [1.2ms total]
Built-In Logs
Structured JSON logs:
{
"level": "info",
"namespace": "orders",
"api": "queue",
"operation": "send",
"latency_ms": 1.2,
"backend": "kafka",
"trace_id": "abc123"
}
Key Takeaways
- Three layers separate concerns: Client API (what), Patterns (how), Backends (where)
- Namespaces provide isolation: Each namespace has independent config and access control
- Interfaces compose backends: Thin interfaces enable type-safe backend composition
- Clients control needs, platform controls implementation: Clear separation prevents misconfiguration
- PII and observability are automatic: No manual implementation required
Next Steps
- Using Prism - Request and configure namespaces
- Data Patterns - Choose the right pattern for your use case