Advanced

CQRS for AI Systems

Apply Command Query Responsibility Segregation to AI systems - separate write models (commands) from read models (queries) for scalable, auditable ML architectures.

What is CQRS?

CQRS separates the write side (commands that change state) from the read side (queries that return data). For AI systems, this means separating the data ingestion/training pipeline from the prediction/serving pipeline - a natural fit since these have fundamentally different performance requirements.

CQRS Applied to AI

AspectCommand Side (Write)Query Side (Read)
PurposeIngest events, update features, retrain modelsServe predictions, return features
LatencyEventual consistency is acceptableSub-millisecond required
ScaleThroughput-optimized (batch)Latency-optimized (real-time)
StorageEvent store, data lake, training datasetsFeature store, model cache, Redis
TechnologyKafka, Spark, AirflowRedis, gRPC, Triton

CQRS + Event Sourcing for ML

# Command side: process events and update state
class MLCommandHandler:
    def __init__(self):
        self.event_store = EventStore()
        self.feature_computer = FeatureComputer()

    async def handle_user_interaction(self, command):
        # Validate command
        event = UserInteractionEvent(
            user_id=command.user_id,
            action=command.action,
            item_id=command.item_id,
            timestamp=datetime.utcnow(),
        )

        # Store event (source of truth)
        await self.event_store.append(event)

        # Update materialized features
        await self.feature_computer.update(event)

        # Publish for downstream consumers
        await self.event_bus.publish("user-interactions", event)


# Query side: serve predictions from optimized read models
class MLQueryHandler:
    def __init__(self):
        self.feature_store = OnlineFeatureStore()  # Redis-backed
        self.model = load_model("recommendation-v3")

    async def get_recommendations(self, query):
        # Read from optimized read model
        features = await self.feature_store.get(query.user_id)
        predictions = self.model.predict(features)
        return RankingResponse(items=predictions.top_k(10))
💡
Eventual consistency is okay: In CQRS, the read model is eventually consistent with the write model. For AI, this means a new user interaction might take a few seconds to influence recommendations. This trade-off is almost always acceptable and enables much better scalability.

Projections for ML

Projections build read-optimized views from event streams. For ML, projections create materialized feature tables:

# Projection: build user feature table from events
class UserFeatureProjection:
    """Reads user events and maintains a materialized feature table"""

    async def process(self, event):
        if event.type == "UserInteractionEvent":
            user_features = await self.read_store.get(event.user_id)

            # Update features
            user_features.total_interactions += 1
            user_features.last_active = event.timestamp
            user_features.category_counts[event.category] += 1

            # Compute derived features
            user_features.engagement_score = compute_engagement(
                user_features.total_interactions,
                user_features.session_count,
                user_features.last_active,
            )

            # Write to read-optimized store
            await self.read_store.set(event.user_id, user_features)

Benefits of CQRS for AI

Independent Scaling

Scale prediction serving independently from data ingestion. Add more read replicas during peak hours without affecting the write pipeline.

Model Versioning

Deploy new model versions as new read models. Run multiple versions simultaneously. Roll back by switching the active read model.

Replay and Reprocess

Rebuild any read model by replaying events from the event store. Recompute features with new logic without losing historical data.

Audit and Compliance

The event store provides a complete audit trail. Explain any prediction by replaying the events and features that produced it.

CQRS adds complexity: Separate read and write models mean eventual consistency, more infrastructure, and more code to maintain. Only adopt CQRS when you have clear scaling or auditability requirements that justify the added complexity. Most small AI systems do fine without it.

Ready to Go Deeper?

Live instructor-led courses from our partners. Affiliate disclosure.