CONTEXT.md: domain model with entities (WidgetConfig, DataSource, LayoutPreset), value objects (Layout, LayoutNode, KeyMapping, WidgetState, Sizing), architecture decisions (CQRS, static dispatch, postcard), client domain model, and design constraints.
1.4 KiB
0001 — Event-driven CQRS
Status: accepted Date: 2026-06-18
Context
K-Frame has a natural write/read split: the write model is config (widgets, data sources, layout) mutated via web UI, the read model is runtime state (current widget data, active layout) pushed to display clients. We need a mechanism to bridge the two — when config changes, runtime behavior must update (restart polling loops, push new layout to clients).
Decision
Full event-driven CQRS. Commands mutate config and emit domain events. The read side projects current state from events and DataSource poll results. Events drive side effects: restarting poll loops, pushing updates to connected clients.
Alternatives considered
Imperative approach — commands mutate config, then application layer imperatively calls "restart poll loop" / "push to clients." Simpler, but tangles command handling with side-effect orchestration. Adding new reactions to config changes requires modifying command handlers.
Consequences
- Clean separation: command handlers only mutate config and emit events, never trigger side effects directly.
- New reactions to config changes are just new event listeners — no command handler modification.
- More moving parts than the imperative approach — event bus, event handlers, projections.
- Events are in-memory only (no event store / event sourcing). This is CQRS, not ES.