### Example Event Headers Source: https://geteventflow.net/integration/rabbitmq This example shows the structure of EventFlow metadata sent in RabbitMQ message headers as a Dictionary. ```text event_name: user-registered event_version: 1 aggregate_name: customer aggregate_id: customer-5b0d9af0 aggregate_sequence_number: 42 event_id: 01JF2ZNKX1QZS5CJ1V6AQ13RPT timestamp: 2025-09-20T17:53:41.2012129Z ``` -------------------------------- ### Install EventFlow.PostgreSql NuGet package Source: https://geteventflow.net/integration/postgresql Command to add the PostgreSQL integration package to your .NET project. ```bash dotnet add package EventFlow.PostgreSql ``` -------------------------------- ### Start MongoDB Container for Development Source: https://geteventflow.net/integration/mongodb Use Docker to spin up a local MongoDB instance for development purposes. ```bash docker run --rm -p 27017:27017 --name eventflow-mongo mongo:7 ``` -------------------------------- ### RabbitMQ Routing Key Example Source: https://geteventflow.net/basics/subscribers An example of a generated routing key for a specific aggregate and event. ```text eventflow.domainevent.my-user.create-user.1 ``` -------------------------------- ### Start a Local PostgreSQL Container Source: https://geteventflow.net/integration/postgresql Docker command to run a disposable PostgreSQL instance for local development. ```bash docker run --rm -p 5432:5432 --name eventflow-postgres \ -e POSTGRES_PASSWORD=eventflow \ -e POSTGRES_DB=eventflow \ postgres:16 ``` -------------------------------- ### Install EventFlow MSSQL NuGet package Source: https://geteventflow.net/integration/mssql Add the required integration package to your project via the .NET CLI. ```bash dotnet add package EventFlow.MsSql ``` -------------------------------- ### Produce Clean JSON Event Example Source: https://geteventflow.net/additional/dos-and-donts An example of a create user event serialized into clean JSON, suitable for event storage. Ensure no type information or value object hints are included. ```json { "Username": "root", "PasswordHash": "1234567890ABCDEF", "EMail": "root@example.org" } ``` -------------------------------- ### Example Application Test Source: https://geteventflow.net/getting-started This test demonstrates publishing a command, fetching a read model, and asserting its state. It configures EventFlow with defaults and an in-memory read store. ```csharp public class ExampleTests { [Test] public async Task ExampleTest() { // Arrange var services = new ServiceCollection(); services.AddEventFlow(ef => ef .AddDefaults(typeof(ExampleAggregate).Assembly) .UseInMemoryReadStoreFor()); using var serviceProvider = services.BuildServiceProvider(); var commandBus = serviceProvider.GetRequiredService(); var queryProcessor = serviceProvider.GetRequiredService(); var exampleId = ExampleId.New; // Act await commandBus.PublishAsync(new ExampleCommand(exampleId, 42), CancellationToken.None) .ConfigureAwait(false); var exampleReadModel = await queryProcessor.ProcessAsync(new ReadModelByIdQuery(exampleId), CancellationToken.None) .ConfigureAwait(false); // Assert exampleReadModel.MagicNumber.Should().Be(42); } } ``` -------------------------------- ### Install EventFlow Source Generators Source: https://geteventflow.net/additional/source-generation Command to add the required NuGet package to your project. ```bash dotnet add package EventFlow.SourceGenerators ``` -------------------------------- ### Install EventFlow MongoDB NuGet package Source: https://geteventflow.net/integration/mongodb Add the required integration package to your .NET project. ```bash dotnet add package EventFlow.MongoDB ``` -------------------------------- ### Example Event Payload Source: https://geteventflow.net/integration/rabbitmq This is an example of the JSON payload representing the actual event data, identical to what the event store persists. ```json { "UserId": "dcd3f2e1-6f9b-4fcb-8901-9a5f6f2f4c0a", "Email": "customer@example.com", "RegisteredAt": "2025-09-20T17:53:41.197842Z" } ``` -------------------------------- ### Install EventFlow.RabbitMQ Package Source: https://geteventflow.net/integration/rabbitmq Use the dotnet CLI to add the EventFlow.RabbitMQ package to your project. This package provides the necessary integration for publishing events to RabbitMQ. ```bash dotnet add package EventFlow.RabbitMQ ``` -------------------------------- ### Migrate MSSQL Database Tables Source: https://geteventflow.net/additional/snapshots Use this code to initialize the required tables for the MSSQL snapshot store. Execute this during application startup or installation. ```csharp var msSqlDatabaseMigrator = serviceProvider.GetRequiredService(); EventFlowSnapshotStoresMsSql.MigrateDatabase(msSqlDatabaseMigrator); ``` -------------------------------- ### Initialize EventFlow with Defaults Source: https://geteventflow.net/getting-started Use AddEventFlow with AddDefaults to bootstrap EventFlow and configure essential components like in-memory event stores and null snapshot stores. This is the starting point for EventFlow configuration. ```csharp var services = new ServiceCollection(); services.AddEventFlow(ef => ef.AddDefaults(typeof(Startup).Assembly)); using var serviceProvider = services.BuildServiceProvider(); ``` -------------------------------- ### Configure Redis Event Store with EventFlow Source: https://geteventflow.net/integration/redis Use this to configure Redis as your event store. Ensure the EventFlow.Redis NuGet package is installed. ```csharp options.ConfigureRedis(connectionString); // or options.ConfigureRedis(IConnectionMultiplexer); ``` -------------------------------- ### Execute a Query using IQueryProcessor in C# Source: https://geteventflow.net/basics/queries Use the IQueryProcessor to execute a query in your application. This example demonstrates processing a GetUserByUsernameQuery. ```csharp var user = await _queryProcessor.ProcessAsync( new GetUserByUsernameQuery("root"), cancellationToken); ``` -------------------------------- ### Migrate MSSQL Databases Source: https://geteventflow.net/integration/event-stores Execute this code to create the necessary tables for the MSSQL event store. This should be done on application start or install/update. ```csharp var serviceProvider = serviceCollection.BuildServiceProvider(); var msSqlDatabaseMigrator = serviceProvider.GetRequiredService(); await EventFlowEventStoresMsSql.MigrateDatabaseAsync( msSqlDatabaseMigrator, CancellationToken.None); ``` -------------------------------- ### Start Saga with OrderCreated Event Source: https://geteventflow.net/basics/sagas Define an OrderSaga that starts when an OrderCreated event is emitted by the OrderAggregate. This saga then publishes a MakeReservation command. ```csharp public class OrderSaga : AggregateSaga, ISagaIsStartedBy { public Task HandleAsync( IDomainEvent domainEvent, ISagaContext sagaContext, CancellationToken cancellationToken) { // Update saga state with useful details. Emit(new OrderStarted(/*...*/)); // Make the reservation Publish(new MakeReservation(/*...*/)); return Task.FromResult(0); } public void Apply(OrderStarted e) { // Update our aggregate state with relevant order details } } ``` -------------------------------- ### Implement a Query Handler in C# Source: https://geteventflow.net/basics/queries Implement the query handler that defines how a query is processed. This example shows a handler for retrieving a user by username using a repository. ```csharp public class GetUserByUsernameQueryHandler : IQueryHandler { private readonly IUserReadModelRepository _userReadModelRepository; public GetUserByUsernameQueryHandler( IUserReadModelRepository userReadModelRepository) { _userReadModelRepository = userReadModelRepository; } public Task ExecuteQueryAsync( GetUserByUsernameQuery query, CancellationToken cancellationToken) { return _userReadModelRepository.GetByUsernameAsync( query.Username, cancellationToken); } } ``` -------------------------------- ### Configure Microsoft SQL Server Snapshot Store Source: https://geteventflow.net/additional/snapshots Configure the MSSQL snapshot store by installing the EventFlow.MsSql NuGet package and using the ConfigureMsSql and UseMsSqlSnapshotStore extension methods. Provide your SQL Server connection string. ```csharp public void ConfigureServices(IServiceCollection services) { services.AddEventFlow(ef => { ef.ConfigureMsSql(MsSqlConfiguration.New .SetConnectionString(@"Server=.\SQLEXPRESS;Database=MyApp;User Id=sa;Password=???")) .UseMsSqlSnapshotStore(); }); } ``` -------------------------------- ### Register a Query Handler in C# Source: https://geteventflow.net/basics/queries Register the query handler with EventFlow. This example shows the basic registration; consider using assembly scanning for more complex scenarios. ```csharp //... .AddQueryHandler(); //... ``` -------------------------------- ### Create and Bind RabbitMQ Queue Source: https://geteventflow.net/integration/rabbitmq Manually declare a RabbitMQ exchange and queue, then bind the queue to the exchange using a routing key pattern. This setup is required before EventFlow can publish messages. Ensure the exchange is declared as durable and of type Topic. ```csharp using var connection = factory.CreateConnection(); using var channel = connection.CreateModel(); channel.ExchangeDeclare("eventflow", ExchangeType.Topic, durable: true); channel.QueueDeclare("customer-updates", durable: true, exclusive: false, autoDelete: false); channel.QueueBind("customer-updates", "eventflow", "eventflow.domainevent.customer.#. "); ``` -------------------------------- ### Example Event Data JSON Source: https://geteventflow.net/getting-started This JSON represents the serialized data of an example event, containing a 'MagicNumber' property. It's a straightforward serialization of the event object. ```json { "MagicNumber": 42 } ``` -------------------------------- ### Implement Saga Locator Source: https://geteventflow.net/basics/sagas Implement ISagaLocator to map domain events to a saga identity. This example adds the order ID to event metadata for saga identification. ```csharp public class OrderSagaLocator : ISagaLocator { public Task LocateSagaAsync( IDomainEvent domainEvent, CancellationToken cancellationToken) { var orderId = domainEvent.Metadata["order-id"]; var orderSagaId = new OrderSagaId($"ordersaga-{orderId}"); return Task.FromResult(orderSagaId); } } ``` -------------------------------- ### Generate Identity instances Source: https://geteventflow.net/basics/identity Use static factory methods to generate new identity instances using different Guid strategies. ```csharp // Uses the default Guid.NewGuid() var testId = TestId.New; ``` ```csharp // Create a namespace, put this in a constant somewhere var emailNamespace = Guid.Parse("769077C6-F84D-46E3-AD2E-828A576AAAF3"); // Creates an identity with the value "test-9181a444-af25-567e-a866-c263b6f6119a" var testId = TestId.NewDeterministic(emailNamespace, "test@example.com"); ``` ```csharp // Creates a new identity every time, but an identity when used in // database indexes, minimizes fragmentation var testId = TestId.NewComb(); ``` -------------------------------- ### Define a Query Object in C# Source: https://geteventflow.net/basics/queries Create a query object that encapsulates the data needed for a query. This example defines a query to search for users by username. ```csharp public class GetUserByUsernameQuery : IQuery { public string Username { get; } public GetUserByUsernameQuery(string username) { Username = username; } } ``` -------------------------------- ### Load Events via IEventStore (Manual vs Generated) Source: https://geteventflow.net/additional/source-generation Comparison of IEventStore load syntax without and with source generators. ```csharp var events = await eventStore.LoadEventsAsync( id, cancellationToken); ``` ```csharp var events = await eventStore.LoadEventsAsync(id, cancellationToken); ``` -------------------------------- ### Enable PostgreSQL snapshot store Source: https://geteventflow.net/integration/postgresql Configure the snapshot store and run the required migration for the EventFlowSnapshots table. ```csharp services.AddEventFlow(o => o.ConfigurePostgreSql(postgres) .UsePostgreSqlSnapshotStore()); await EventFlowSnapshotStoresPostgreSql.MigrateDatabaseAsync(migrator, cancellationToken); ``` -------------------------------- ### Configure Files Event Store Source: https://geteventflow.net/integration/event-stores Use the `UseFilesEventPersistence()` method with an `IFilesEventStoreConfiguration` to set up the file-based event store for testing purposes. Specify the store path. ```csharp var storePath = @"c:\\eventstore"; var serviceCollection = new ServiceCollection(); serviceCollection.AddEventFlow(eventFlowOptions => { // Other details are omitted for clarity eventFlowOptions.UseFilesEventPersistence( FilesEventStoreConfiguration.Create(storePath)); }); ``` -------------------------------- ### Provision PostgreSQL schema Source: https://geteventflow.net/integration/postgresql Run the database migration to create necessary tables and types for EventFlow. ```csharp await using var scope = services.BuildServiceProvider().CreateAsyncScope(); var migrator = scope.ServiceProvider.GetRequiredService(); await EventFlowEventStoresPostgreSql.MigrateDatabaseAsync(migrator, cancellationToken); ``` -------------------------------- ### Register a job in EventFlow Source: https://geteventflow.net/basics/jobs Registers custom job types within the EventFlow configuration during service setup. ```csharp public void ConfigureServices(IServiceCollection services) { services.AddEventFlow(ef => { ef.AddJobs(typeof(LogMessageJob)); }); } ``` -------------------------------- ### Enable PostgreSQL event store Source: https://geteventflow.net/integration/postgresql Configure the event store to use PostgreSQL instead of the default in-memory store. ```csharp services.AddEventFlow(o => o.ConfigurePostgreSql(postgres) .UsePostgreSqlEventStore()); ``` -------------------------------- ### Configure Elasticsearch Read Store in C# Source: https://geteventflow.net/integration/read-stores Set up the Elasticsearch read model store with `UseElasticsearchReadModel<>` or `UseElasticsearchReadModel<,>`. Configure Elasticsearch connection details using `ConfigureElasticsearch(...)`. Ensure read model mappings are created in Elasticsearch beforehand and that each read model has a separate index. ```csharp // ... .ConfigureElasticsearch(new Uri("http://localhost:9200/")) // ... .UseElasticsearchReadModel() .UseElasticsearchReadModel() // ... ``` -------------------------------- ### Configure RabbitMQ Publisher in EventFlow Source: https://geteventflow.net/integration/rabbitmq Enable the RabbitMQ publisher by configuring EventFlowOptions during application startup. Specify the RabbitMQ connection URI, persistence, channel pooling, and the target exchange name. Ensure the RabbitMQ exchange is pre-provisioned. ```csharp using EventFlow.RabbitMQ; using EventFlow.RabbitMQ.Extensions; var rabbitUri = new Uri("amqp://guest:guest@localhost:5672/"); services.AddEventFlow(options => options // ... register aggregates, commands, read models, etc. .PublishToRabbitMq( RabbitMqConfiguration.With( rabbitUri, persistent: true, // mark messages as durable modelsPrConnection: 5, // pooled channels per connection exchange: "eventflow"))); // topic exchange to publish to ``` -------------------------------- ### Define SQL Read Model Schema Source: https://geteventflow.net/integration/mssql Example T-SQL schema for a read model table, requiring an identity column and a version column for concurrency tracking. ```sql CREATE TABLE [dbo].[ReadModel-UserReadModel] ( [Id] NVARCHAR(255) NOT NULL, [Version] INT NOT NULL, [UserId] NVARCHAR(255) NOT NULL, [Username] NVARCHAR(255) NOT NULL, CONSTRAINT [PK_ReadModel-UserReadModel] PRIMARY KEY CLUSTERED ([Id]) ); ``` -------------------------------- ### Runtime Connection String Fetching Source: https://geteventflow.net/migrations/v0-to-v1 Implement `IMsSqlConfiguration` with a custom `GetConnectionStringAsync` method to fetch connection strings at runtime from external sources. ```csharp Task GetConnectionStringAsync( Label label, string name, CancellationToken cancellationToken); ``` -------------------------------- ### Return Execution Results Source: https://geteventflow.net/basics/commands Demonstrates how to return success or failure states from command execution. ```csharp ExecutionResult.Success(); ExecutionResult.Failed(); ExecutionResult.Failed("With some error"); ``` -------------------------------- ### Create a PostgreSQL Read Model Table Source: https://geteventflow.net/integration/postgresql SQL DDL script to create the table and index for the User read model. ```sql CREATE TABLE IF NOT EXISTS "ReadModel-User" ( Id BIGINT GENERATED BY DEFAULT AS IDENTITY, AggregateId VARCHAR(64) NOT NULL, CreateTime TIMESTAMPTZ NOT NULL, UpdatedTime TIMESTAMPTZ NOT NULL, LastAggregateSequenceNumber INT NOT NULL, DisplayName TEXT NOT NULL, CONSTRAINT "PK_ReadModel-User" PRIMARY KEY (Id) ); CREATE INDEX IF NOT EXISTS "IX_ReadModel-User_AggregateId" ON "ReadModel-User" (AggregateId); ``` -------------------------------- ### ExampleAggregate Implementation Source: https://geteventflow.net/getting-started Defines an aggregate root that can have its magic number set once. State changes are applied via the Apply method when events are emitted. ```csharp public class ExampleAggregate : AggregateRoot, IEmit { private int? _magicNumber; public ExampleAggregate(ExampleId id) : base(id) { } public IExecutionResult SetMagicNumber(int magicNumber) { if (_magicNumber.HasValue) { return ExecutionResult.Failed("Magic number already set"); } Emit(new ExampleEvent(magicNumber)); return ExecutionResult.Success(); } public void Apply(ExampleEvent aggregateEvent) { _magicNumber = aggregateEvent.MagicNumber; } } ``` -------------------------------- ### Basic Username Value Object Source: https://geteventflow.net/additional/value-objects A standard C# class representing a username with basic validation. Use this as a starting point before applying EventFlow specific enhancements. ```csharp public class Username { public string Value { get; } public Username(string value) { if (string.IsNullOrEmpty(value) || value.Length <= 4) { throw DomainError.With($"Invalid username '{value}'"); } Value = value; } } ``` -------------------------------- ### Example Event Metadata JSON Source: https://geteventflow.net/getting-started This JSON shows the metadata stored by EventFlow alongside an event. It includes details like timestamp, sequence number, aggregate information, and event identifiers. ```json { "timestamp": "2016-11-09T20:56:28.5019198+01:00", "aggregate_sequence_number": "1", "aggregate_name": "ExampleAggregate", "aggregate_id": "example-c1d4a2b1-c75b-4c53-ae44-e67ee1ddfd79", "event_id": "event-d5622eaa-d1d3-5f57-8023-4b97fabace90", "timestamp_epoch": "1478721389", "batch_id": "52e9d7e9-3a98-44c5-926a-fc416e20556c", "source_id": "command-69176516-07b7-4142-beaf-dba82586152c", "event_name": "example", "event_version": "1" } ``` -------------------------------- ### Publishing a Command with ICommandBus Source: https://geteventflow.net/getting-started Publish commands using the ICommandBus. Ensure the command is correctly instantiated with its required parameters. ```csharp await commandBus.PublishAsync(new ExampleCommand(exampleId, 42), CancellationToken.None) .ConfigureAwait(false); ``` -------------------------------- ### Configure PostgreSQL Event Store Source: https://geteventflow.net/integration/event-stores Use the `UsePostgreSqlEventStore()` method to configure EventFlow to use PostgreSQL as the event store. ```csharp var serviceCollection = new ServiceCollection(); serviceCollection.AddEventFlow(eventFlowOptions => { // Other details are omitted for clarity eventFlowOptions.UsePostgreSqlEventStore(); }); ``` -------------------------------- ### Implement a basic UserReadModel Source: https://geteventflow.net/integration/read-stores A read model that handles the UserCreated event to store username and user ID. ```csharp public class UserReadModel : IReadModel, IAmReadModelFor { public string UserId { get; set; } public string Username { get; set; } public Task ApplyAsync( IReadModelContext context, IDomainEvent domainEvent, CancellationToken cancellationToken) { UserId = domainEvent.AggregateIdentity.Value; Username = domainEvent.AggregateEvent.Username.Value; return Task.CompletedTask; } } ``` -------------------------------- ### Initialize EventFlow with IServiceCollection reference Source: https://geteventflow.net/migrations/v0-to-v1 Use this traditional approach to pass an existing service collection reference to EventFlowOptions. ```csharp var eventFlowOptions = EventFlowOptions.New(serviceCollection) // Set up EventFlow here ``` -------------------------------- ### Configure Redis Read Store in C# Source: https://geteventflow.net/integration/read-stores Enable Redis as a read model store using `UseRedisReadModel<>` or `UseRedisReadModel<,>`. Requires Redis Search and Redis JSON modules. Fields intended for querying other than ID must have the `[Indexed]` attribute. ```csharp // ... .UseRedisReadModel() .UseRedisReadModel() // ... ``` -------------------------------- ### Configure In-Memory Read Store in C# Source: https://geteventflow.net/integration/read-stores Use `UseInMemoryReadStoreFor<>` or `UseInMemoryReadStoreFor<,>` to configure the in-memory read model store. This is suitable for testing as all read models are lost on restart. ```csharp public void ConfigureServices(IServiceCollection services) { services.AddEventFlow(ef => { ef.UseInMemoryReadStoreFor(); ef.UseInMemoryReadStoreFor(); }); } ``` -------------------------------- ### Configure EventFlow with MSSQL Source: https://geteventflow.net/integration/mssql Register MSSQL services and stores within the IServiceCollection. Ensure ConfigureMsSql is called before registering specific stores. ```csharp public void ConfigureServices(IServiceCollection services) { services.AddEventFlow(options => { options .ConfigureMsSql(MsSqlConfiguration .New .SetConnectionString(@"Server=.\SQLEXPRESS;Database=MyApp;User Id=sa;Password=Pa55w0rd!")) .UseMssqlEventStore() .UseMssqlSnapshotStore() .UseMssqlReadModel() .UseMssqlReadModel(); }); } ``` -------------------------------- ### Register the Secured Command Bus Source: https://geteventflow.net/additional/faq Replace the default ICommandBus registration with the custom decorator using the service collection. ```csharp services.AddEventFlow(options => { /* configure aggregates, commands, ... */ }); services.Replace(ServiceDescriptor.Transient(sp => new SecuredCommandBus( ActivatorUtilities.CreateInstance(sp), sp.GetRequiredService(), sp.GetRequiredService()))); ``` -------------------------------- ### Configure EventFlow with MongoDB Source: https://geteventflow.net/integration/mongodb Register the MongoDB client and configure event stores, snapshot stores, and read models in your DI container. ```csharp // Program.cs / Startup.cs var mongoUrl = new MongoUrl(Configuration.GetConnectionString("eventflow-mongo")); var mongoClient = new MongoClient(mongoUrl); services.AddEventFlow(ef => ef .ConfigureMongoDb(mongoClient, mongoUrl.DatabaseName) .UseMongoDbEventStore() // Events .UseMongoDbSnapshotStore() // Snapshots (optional) .UseMongoDbReadModel() // Read models .UseMongoDbReadModel()); ``` -------------------------------- ### Update Aggregate via IAggregateStore (Manual vs Generated) Source: https://geteventflow.net/additional/source-generation Comparison of IAggregateStore update syntax without and with source generators. ```csharp await aggregateStore.UpdateAsync( id, SourceId.New, (order, _) => order.DoSomething(), cancellationToken); ``` ```csharp await aggregateStore.UpdateAsync( id, order => order.DoSomething(), cancellationToken); ``` -------------------------------- ### Enable MSSQL Event Store Source: https://geteventflow.net/integration/mssql Register the MSSQL event store to replace the default in-memory implementation. ```csharp services.AddEventFlow(o => o.ConfigureMsSql(config) .UseMssqlEventStore()); ``` -------------------------------- ### Initialize MongoDB Event Persistence Source: https://geteventflow.net/integration/mongodb Run the initializer to ensure required indexes are created in the events collection. ```csharp using (var scope = services.BuildServiceProvider().CreateScope()) { scope.ServiceProvider .GetRequiredService() .Initialize(); } ``` -------------------------------- ### Initialize EventFlow via IServiceCollection extension Source: https://geteventflow.net/migrations/v0-to-v1 Use this fluent extension method to configure EventFlow within an existing service collection. ```csharp serviceCollection.AddEventFlow(o => // Set up EventFlow here ); ``` -------------------------------- ### Migrate MSSQL Event Store Schema Source: https://geteventflow.net/integration/mssql Run the idempotent database migrator to provision the necessary tables and types for the event store. ```csharp using var serviceProvider = services.BuildServiceProvider(); var migrator = serviceProvider.GetRequiredService(); await EventFlowEventStoresMsSql.MigrateDatabaseAsync(migrator, cancellationToken); ``` -------------------------------- ### Declare Subscribers (Manual vs Generated) Source: https://geteventflow.net/additional/source-generation Comparison of subscriber declaration syntax without and with source generators. ```csharp public class OrderAggregateSubscribers : ISubscribeSynchronousTo ``` ```csharp public class OrderAggregateSubscribers : ISubscribeSynchronousTo ``` -------------------------------- ### Implement a UserNicknameReadModel Source: https://geteventflow.net/integration/read-stores A read model representing an individual nickname. ```csharp public class UserNicknameReadModel : IReadModel, IAmReadModelFor { public string UserId { get; set; } public string Nickname { get; set; } public Task ApplyAsync( IReadModelContext context, IDomainEvent domainEvent, CancellationToken cancellationToken) { UserId = domainEvent.AggregateIdentity.Value; Nickname = domainEvent.AggregateEvent.Nickname.Value; return Task.CompletedTask; } } ``` -------------------------------- ### Implement IMongoDbReadModel Source: https://geteventflow.net/integration/mongodb Define a read model that implements IMongoDbReadModel for optimistic concurrency support. ```csharp [MongoDbCollectionName("users")] public class UserReadModel : IMongoDbReadModel, IAmReadModelFor { public string Id { get; set; } = default!; // MongoDB document _id public long? Version { get; set; } public string Username { get; set; } = default!; public Task ApplyAsync( IReadModelContext context, IDomainEvent domainEvent, CancellationToken cancellationToken) { Id = domainEvent.AggregateIdentity.Value; Username = domainEvent.AggregateEvent.Username.Value; return Task.CompletedTask; } } ``` -------------------------------- ### Configure PostgreSQL in EventFlow Source: https://geteventflow.net/integration/postgresql Register the PostgreSQL connection, migrator, and stores within the ConfigureServices method. ```csharp public void ConfigureServices(IServiceCollection services) { var postgres = PostgreSqlConfiguration.New .SetConnectionString(Configuration.GetConnectionString("eventflow-postgres")) .SetTransientRetryCount(3); services.AddEventFlow(o => o .ConfigurePostgreSql(postgres) .UsePostgreSqlEventStore() // Events .UsePostgreSqlSnapshotStore() // Snapshots (optional) .UsePostgreSqlReadModel() // Read models .UsePostgreSqlReadModel()); } ``` -------------------------------- ### Configure MSSQL Event Store Source: https://geteventflow.net/integration/event-stores Use the `UseMssqlEventStore()` method to configure EventFlow to use Microsoft SQL Server as the event store. Ensure the database and tables are set up beforehand. ```csharp var serviceCollection = new ServiceCollection(); serviceCollection.AddEventFlow(eventFlowOptions => { // Other details are omitted for clarity eventFlowOptions.UseMssqlEventStore(); }); ``` -------------------------------- ### Configure MongoDB Read Store in C# Source: https://geteventflow.net/integration/read-stores Configure MongoDB as a read model store by calling `UseMongoDbReadModel<>` or `UseMongoDbReadModel<,>` with your read model type. ```csharp // ... .UseMongoDbReadModel() .UseMongoDbReadModel() // ... ``` -------------------------------- ### Enable MSSQL Snapshot Store Source: https://geteventflow.net/integration/mssql Register the MSSQL snapshot store to persist aggregate snapshots. ```csharp services.AddEventFlow(o => o.ConfigureMsSql(config) .UseMssqlSnapshotStore()); ``` -------------------------------- ### Execute Embedded Migration Scripts Source: https://geteventflow.net/integration/mssql Run custom SQL migration scripts embedded in the assembly during application startup. ```csharp await migrator.MigrateDatabaseUsingEmbeddedScriptsAsync( typeof(Program).Assembly, scriptNamespace: "MyCompany.MyApp.SqlScripts", cancellationToken); ``` -------------------------------- ### Complete Saga with PaymentAccepted Event Source: https://geteventflow.net/basics/sagas Handle the PaymentAccepted event to confirm the order and complete the saga. The saga is completed by calling the Complete() method. ```csharp public class OrderSaga : AggregateSaga, // ... ISagaHandles { // ... public Task HandleAsync( IDomainEvent domainEvent, ISagaContext sagaContext, CancellationToken cancellationToken) { Emit(new OrderConfirmed(/*...*/)) } public void Apply(OrderConfirmed e) { // As this is the last event, we complete the saga by calling Complete() Complete(); } } ``` -------------------------------- ### Implement Snapshot Upgraders Source: https://geteventflow.net/additional/snapshots Create classes that implement ISnapshotUpgrader to map older snapshot versions to newer ones. Ensure your upgrader classes are registered with EventFlow. ```csharp public class UserSnapshotV1ToV2Upgrader : ISnapshotUpgrader { public Task UpgradeAsync( UserSnapshotV1 userSnapshotV1, CancellationToken cancellationToken) { // Map from V1 to V2 and return return Task.FromResult(new UserSnapshotV2 { // Initialize properties with the mapped values from V1 }); } } ``` ```csharp public class UserSnapshotV2ToV3Upgrader : ISnapshotUpgrader { public Task UpgradeAsync( UserSnapshotV2 userSnapshotV2, CancellationToken cancellationToken) { // Map from V2 to V3 and return return Task.FromResult(new UserSnapshot { // Initialize properties with the mapped values from V2 }); } } ``` -------------------------------- ### ExampleEvent Definition Source: https://geteventflow.net/getting-started Represents a domain event indicating that a magic number has been set. It includes versioning information using the [EventVersion] attribute, which is crucial for deserialization. ```csharp [EventVersion("example", 1)] public class ExampleEvent : AggregateEvent { public ExampleEvent(int magicNumber) { MagicNumber = magicNumber; } public int MagicNumber { get; } } ``` -------------------------------- ### Implement Idempotent Command Source: https://geteventflow.net/basics/commands Uses ISourceId in the command constructor to ensure idempotency and prevent duplicate operations. ```csharp public class UserUpdatePasswordCommand : Command { public Password NewPassword { get; } public Password OldPassword { get; } public UserCreateCommand( UserId id, ISourceId sourceId, Password newPassword, Password oldPassword) : base(id, sourceId) { NewPassword = newPassword; OldPassword = oldPassword; } } ``` -------------------------------- ### Register PostgreSQL read model store Source: https://geteventflow.net/integration/postgresql Plug the SQL read-store implementation into EventFlow for specific read models. ```csharp services.AddEventFlow(o => o.ConfigurePostgreSql(postgres) .UsePostgreSqlReadModel() .UsePostgreSqlReadModel()); ``` -------------------------------- ### Initialize EventFlow with internal IServiceCollection Source: https://geteventflow.net/migrations/v0-to-v1 Allows EventFlow to instantiate its own service collection. Recommended for small tests only and should not be used in production. ```csharp var eventFlowOptions = EventFlowOptions.New() // its a short hand for // var eventFlowOptions = EventFlowOptions.New(new ServiceCollection()) // Set up EventFlow here ``` -------------------------------- ### Execute Read Model Migrations Source: https://geteventflow.net/integration/postgresql Uses IPostgreSqlDatabaseMigrator to apply embedded SQL scripts to the database. ```csharp var migrator = scope.ServiceProvider.GetRequiredService(); await migrator.MigrateDatabaseUsingEmbeddedScriptsAsync( typeof(Program).Assembly, scriptNamespace: "MyCompany.MyApp.SqlScripts", cancellationToken); ``` -------------------------------- ### Implement a UserNicknameReadModelLocator Source: https://geteventflow.net/integration/read-stores Maps a domain event to a specific read model ID based on the nickname ID. ```csharp public class UserNicknameReadModelLocator : IReadModelLocator { public IEnumerable GetReadModelIds(IDomainEvent domainEvent) { var userNicknameAdded = domainEvent as IDomainEvent; if (userNicknameAdded == null) { yield break; } yield return userNicknameAdded.Nickname.Id; } } ``` -------------------------------- ### Create an Aggregate Root Source: https://geteventflow.net/basics/aggregates Inherit from AggregateRoot<,> and pass the aggregate type and identity type as generic arguments. ```csharp public class TestAggregate : AggregateRoot { public TestAggregate(TestId id) : base(id) { } } ``` -------------------------------- ### Implement a Specification Source: https://geteventflow.net/additional/specifications Create a custom specification by inheriting from Specification and overriding IsNotSatisfiedBecause to provide validation reasons. ```csharp public class BelowFiveSpecification : Specification { protected override IEnumerable IsNotSatisfiedBecause(int i) { if (5 <= i) { yield return string.Format("{0} is not below five", i); } } } ``` -------------------------------- ### Combine Specifications Source: https://geteventflow.net/additional/specifications Use extension methods to combine multiple specifications using logical operators or to throw domain errors. ```csharp // Throws a `DomainError` exception if obj doesn't satisfy the specification spec.ThrowDomainErrorIfNotStatisfied(obj); // Builds a new specification that requires all input specifications to be // satified var allSpec = specEnumerable.All(); // Builds a new specification that requires a predefined amount of the // input specifications to be satisfied var atLeastSpec = specEnumerable.AtLeast(4); // Builds a new specification that requires the two input specifications // to be satisfied var andSpec = spec1.And(spec2); // Builds a new specification that requires one of the two input // specifications to be satisfied var orSpec = spec1.Or(spec2); // Builds a new specification that requires the input specification // not to be satisfied var notSpec = spec.Not(); ``` -------------------------------- ### Implement a Secured Command Bus Decorator Source: https://geteventflow.net/additional/faq Create a decorator for ICommandBus to enforce authorization logic before delegating to the inner bus. This implementation requires an ICommandAuthorizer and ICurrentUser to validate command execution. ```csharp public sealed class SecuredCommandBus : ICommandBus { private readonly ICommandBus _inner; private readonly ICommandAuthorizer _authorizer; private readonly ICurrentUser _user; public SecuredCommandBus(ICommandBus inner, ICommandAuthorizer authorizer, ICurrentUser user) { _inner = inner; _authorizer = authorizer; _user = user; } public Task PublishAsync( ICommand command, CancellationToken cancellationToken) where TAggregate : IAggregateRoot where TIdentity : IIdentity where TExecutionResult : IExecutionResult { if (!_authorizer.CanExecute(command, _user)) { throw DomainError.With( "Command {0} is not allowed for {1}", command.GetType().Name, _user.Id); } return _inner.PublishAsync(command, cancellationToken); } } ``` -------------------------------- ### RabbitMQ Routing Key Format Source: https://geteventflow.net/basics/subscribers The default routing key structure for domain events. ```text eventflow.domainevent.[Aggregate name].[Event name].[Event version] ``` -------------------------------- ### Defining an EventFlow Command Source: https://geteventflow.net/getting-started Commands are value objects implementing ICommand. Use the provided base class for easy implementation, housing arguments for command execution. ```csharp public class ExampleCommand : Command { public ExampleCommand(ExampleId aggregateId, int magicNumber) : base(aggregateId) { MagicNumber = magicNumber; } public int MagicNumber { get; } } ``` -------------------------------- ### Implement a UserUpdatePasswordCommand Source: https://geteventflow.net/basics/commands Defines a command class inheriting from Command to encapsulate password update data. ```csharp public class UserUpdatePasswordCommand : Command { public Password NewPassword { get; } public Password OldPassword { get; } public UserUpdatePasswordCommand( UserId id, Password newPassword, Password oldPassword) : base(id) { NewPassword = newPassword; OldPassword = oldPassword; } } ``` -------------------------------- ### Implement a custom job Source: https://geteventflow.net/basics/jobs Defines a custom job by implementing the IJob interface and using the JobVersion attribute for serialization. ```csharp [JobVersion("LogMessage", 1)] public class LogMessageJob : IJob { public LogMessageJob(string message) { Message = message; } public string Message { get; } public Task ExecuteAsync( IServiceProvider serviceProvider, CancellationToken cancellationToken) { var log = serviceProvider.GetRequiredService>(); log.LogDebug(Message); return Task.CompletedTask; } } ``` -------------------------------- ### Configure MongoDB Event Store Source: https://geteventflow.net/integration/event-stores Use the `UseMongoDbEventStore()` method to configure EventFlow to use MongoDB as the event store. ```csharp var serviceCollection = new ServiceCollection(); serviceCollection.AddEventFlow(eventFlowOptions => { // Other details are omitted for clarity eventFlowOptions.UseMongoDbEventStore(); }); ``` -------------------------------- ### Migrate MSSQL Snapshot Store Schema Source: https://geteventflow.net/integration/mssql Provision the snapshot store schema using the dedicated migrator. ```csharp var migrator = serviceProvider.GetRequiredService(); await EventFlowSnapshotStoresMsSql.MigrateDatabaseAsync(migrator, cancellationToken); ``` -------------------------------- ### Configure In-Memory Snapshot Store Source: https://geteventflow.net/additional/snapshots Use the UseInMemorySnapshotStore() extension method to configure EventFlow to use an in-memory snapshot store. This is suitable for testing or small applications. ```csharp // ... .UseInMemorySnapshotStore() // ... ``` -------------------------------- ### Manually Load, Modify, and Store Aggregate Source: https://geteventflow.net/basics/aggregates Load an aggregate using LoadAsync, modify its state, and then save the changes using StoreAsync. Provide a unique source ID to prevent duplicate operations. ```csharp // Load the aggregate from the store var testId = TestId.With(id); var aggregate = await aggregateStore.LoadAsync( testId, CancellationToken.None); // Call the method to change the aggregate state aggregate.Ping("ping"); // Save the changes var sourceId = TestId.New; await aggregateStore.StoreAsync( aggregate, sourceId, CancellationToken.None); ``` -------------------------------- ### Load All Events for an Aggregate Source: https://geteventflow.net/basics/aggregates Use IEventStore.LoadEventsAsync to retrieve all events associated with a specific aggregate. This is useful for auditing or replaying events. ```csharp public class TestController(IEventStore eventStore) : ControllerBase { public async Task> GetAuditLog( Guid id, CancellationToken cancellationToken) { var testId = TestId.With(id); var events = await eventStore.LoadEventsAsync( testId, cancellationToken); return events.Select(e => new EventDto { EventType = e.EventType.Name, Timestamp = e.Timestamp }); } } public class EventDto { public required string EventType { get; init; } public required DateTimeOffset Timestamp { get; init; } } ``` -------------------------------- ### Tune read model replay throughput Source: https://geteventflow.net/additional/configuration Adjusts paging sizes for event store loading and read model population. Smaller batches reduce memory usage, while larger batches improve throughput for co-located stores. ```csharp services.AddEventFlow(options => { options.Configure(cfg => { cfg.LoadReadModelEventPageSize = 1000; // event store paging cfg.PopulateReadModelEventPageSize = 2000; // read model batch size }); }); ``` -------------------------------- ### Declare Aggregate Events (Manual vs Generated) Source: https://geteventflow.net/additional/source-generation Comparison of event declaration syntax without and with source generators. ```csharp public class OrderCreated : IAggregateEvent ``` ```csharp public class OrderCreated : OrderAggregateEvent ``` -------------------------------- ### Publish Command with CQRS Pattern Source: https://geteventflow.net/basics/aggregates Use the CQRS pattern by publishing a command via the command bus to change the aggregate state. This decouples the command creation from its execution. ```csharp public class TestController(ICommandBus commandBus) : ControllerBase { public async Task Ping( Guid id, CancellationToken cancellationToken) { var testId = TestId.With(id); // Create a command with the required data var command = new PingCommand(testId) { Data = "ping", }; // Publish the command using the command bus await commandBus.PublishAsync(command, cancellationToken); } } ```