Event-Driven Architecture¶
Target
This page describes intended behaviour or structure. No implementation is claimed unless a section is explicitly marked Current implementation or Evidence.
Reviewed 2026-10-09
The messaging-stack table (Azure Service Bus, MassTransit primary, NServiceBus alternative) is one profile, not the architecture: the port is ConnectSoft.Extensions.MessagingModel and three adapters exist (Orleans with outbox/inbox grains, MassTransit with options only, NServiceBus using its own outbox); the only production consumer chose Orleans (ADR-0012). The transactional-outbox pattern must be proven per profile; no saga beyond a scaffold exists; replay is not promised (ADR-0014).
The factory is event-driven end to end. Asynchronous, observable events are how platforms collaborate without tight coupling, how the lifecycle stays traceable and replayable, and how runtime feedback flows back into knowledge. This page describes the EDA patterns; the Event-Driven Mindset covers the philosophy.
Messaging stack¶
| Concern | Choice |
|---|---|
| Broker | Azure Service Bus |
| Messaging framework | MassTransit (primary); NServiceBus supported alternative |
| Library | ConnectSoft.Extensions.MessagingModel.MassTransit, ConnectSoft.Extensions.Saas.Messaging.MassTransit |
| Envelope | Canonical event envelope |
| In-service project | ...FlowModel.MassTransit |
Event flow¶
flowchart LR
aggregate["Aggregate Root"] -->|emits| domainEvent["Domain Event"]
domainEvent -->|outbox| publisher["MassTransit Publisher"]
publisher --> topic["Azure Service Bus Topic"]
topic --> sub1["Subscription: in-platform"]
topic --> sub2["Subscription: cross-platform"]
sub1 --> consumer1["Platform Consumer"]
sub2 --> integration["Integration Event Consumers"]
integration --> knowledge["Knowledge Platform"]
integration --> observability["Observability & Feedback"]
Patterns¶
| Pattern | Use in the factory |
|---|---|
| Transactional outbox | Aggregates persist state and pending events atomically; a publisher relays them, guaranteeing at-least-once delivery. |
| Topic-per-event-type / topic-per-context | Subscriptions filter on cs-event-type and cs-tenant-id application properties. |
| Saga / process manager | Long-running workflows in the Control Plane use MassTransit sagas correlated by correlationId. |
| Idempotent consumers | Dedup on eventId + idempotency key (see Metadata Schema). |
| Dead-letter + replay | Poison messages dead-letter with the full envelope; replay is supported by Control Plane and Knowledge replay services. |
| Event-carried state transfer | Integration events carry enough payload for consumers to act without synchronous callbacks. |
Correlation and causation¶
Every event carries traceId (lifecycle), correlationId (workflow), and causationId (direct cause). Together they reconstruct the full causal graph of a production run — the basis for Workflow Replay and lineage in the Knowledge Graph.
flowchart LR
e1["ProjectCreated<br/>causation: none"] --> e2["BlueprintValidated<br/>causation: e1"]
e2 --> e3["AgentTaskAssigned<br/>causation: e2"]
e3 --> e4["ArtifactCreated<br/>causation: e3"]
e4 --> e5["PullRequestOpened<br/>causation: e4"]
e5 --> e6["DeploymentPromoted<br/>causation: e5"]
Governance of events¶
Sensitive payloads are classified and may be redacted before crossing platform boundaries (see Knowledge governance). The complete inventory of integration events is the Event Catalog.