Architecture

SourceFlow.Net is an event-driven architecture framework implementing CQRS and Event Sourcing patterns. The system separates command processing from event handling, enabling scalable domain-driven design.

Overview

The architecture follows a clean separation between write-side (commands → sagas → entities) and read-side (events → views → view models), connected through an event queue.

  • Aggregates encapsulate business logic and send commands
  • Command Bus routes commands to appropriate saga handlers
  • Sagas handle commands and maintain consistency
  • Sagas persist entities to the Entity Store
  • Sagas raise events to the Event Queue
  • Event Queue dispatches events to subscribers
  • Views project events into read models (ViewModels)
  • Command Store persists commands for replay

Core Architectural Patterns

PatternWrite SideRead Side
CQRSCommands modify state via CommandBusQueries read from materialised ViewModels
Event SourcingCommands persisted in CommandStoreEvents represent state changes, replay-capable
Saga PatternLong-running business processesCoordinate across aggregates

High-Level Component Diagram

SourceFlow.Net Component Diagram

Command Processing Path

  1. ICommandBus — Entry point for command publishing
  2. CommandBus — Manages command persistence (assigns sequence number) and dispatching
  3. ICommandDispatcher — Routes commands to subscribers
  4. CommandDispatcher — Dispatches to all registered ICommandSubscriber instances
  5. CommandSubscriber — Routes commands to appropriate Sagas
  6. ISaga — Handles commands, persists entities, and produces events
📌 Command Metadata

Every command includes SequenceNo for ordering and IsReplay flag for replay scenarios. The metadata is preserved through the entire pipeline.

Event Processing Path

  1. IEventQueue — Entry point for event publishing (from Sagas)
  2. EventQueue — Manages event distribution
  3. IEventDispatcher — Routes events to subscribers
  4. EventDispatcher — Dispatches to all registered IEventSubscriber instances
  5. Aggregate.EventSubscriber — Routes events to Aggregates implementing ISubscribes<TEvent>
  6. Projections.EventSubscriber — Routes events to Views implementing IProjectOn<TEvent>

Storage Abstractions

LayerInterfaceAdapterPurpose
CommandsICommandStoreICommandStoreAdapterPersist commands with sequence numbers for replay
EntitiesIEntityStoreIEntityStoreAdapterPersist aggregate state
ViewModelsIViewModelStoreIViewModelStoreAdapterPersist materialised views for queries

The adapter pattern provides scoped wrappers around the store interfaces, ensuring proper lifecycle management in DI containers.

Service Lifetimes

LifetimeServices
SingletonIEventQueue, IEventDispatcher, IEventSubscriber, IDomainTelemetryService
ScopedICommandBus, ICommandDispatcher, ICommandSubscriber, ICommandPublisher, Store Adapters
ConfigurableISaga, IAggregate, IView implementations (default: Singleton)
C#
// Configure component lifetime (default: Singleton)
services.UseSourceFlow(ServiceLifetime.Singleton, assemblies);

Built-in Telemetry

The framework includes built-in OpenTelemetry support via IDomainTelemetryService:

OperationTrace Name
Command dispatchsourceflow.commandbus.dispatch
Command replaysourceflow.commandbus.replay
Command sendsourceflow.commanddispatcher.send
Event enqueuesourceflow.eventqueue.enqueue
Event dispatchsourceflow.eventdispatcher.dispatch

Tags include command/event type, entity IDs, sequence numbers, and subscriber counts.

Cloud Extension Points

The interface-based design makes cloud extension seamless:

  • ICommandDispatcher — New implementation for AWS SQS dispatching
  • IEventDispatcher — New implementation for AWS SNS publishing
  • Selective routing — Only cloud-configured commands/events go to cloud; others stay local
  • Maintained local processing — Existing local processing works alongside cloud dispatch
SourceFlow.Net Cloud Extension Diagram
Explore Cloud Integration →

Design Principles

  • Separation of Concerns — Commands, Events, Aggregates, Sagas, and Views are distinct
  • Interface-based Design — All major components use interfaces for extensibility
  • Dependency Inversion — Components depend on abstractions, not implementations
  • Single Responsibility — Each component has a focused purpose
  • Open/Closed Principle — Extensible through new implementations without modifying core