Skip to content

Commit c6b2e28

Browse files
authored
Merge pull request #1124 from eventflow/improve-docs-configuration
Improve documentation for configuration
2 parents 83b5fc8 + b665342 commit c6b2e28

File tree

1 file changed

+137
-12
lines changed

1 file changed

+137
-12
lines changed
Lines changed: 137 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,146 @@
1-
---
21
title: Configuration
32
---
43

5-
# Configuration
4+
# EventFlow runtime configuration
5+
6+
EventFlow ships with sensible defaults, but most production workloads need to tune how the pipeline reacts to retries, subscriber failures, and replay throughput. All of those switches are exposed via `EventFlowOptions` when you wire the framework into your dependency injection container.
7+
8+
## How configuration is applied
9+
10+
Calling `AddEventFlow` registers the core services and gives you an `IEventFlowOptions` hook. Every configuration tweak happens inside that callback.
11+
12+
```csharp
13+
using System;
14+
using Microsoft.Extensions.DependencyInjection;
15+
using EventFlow;
16+
17+
var services = new ServiceCollection();
18+
19+
services.AddEventFlow(options =>
20+
{
21+
options
22+
.ConfigureOptimisticConcurrencyRetry(retries: 6, delayBeforeRetry: TimeSpan.FromMilliseconds(250))
23+
.Configure(cfg =>
24+
{
25+
cfg.ThrowSubscriberExceptions = true;
26+
cfg.IsAsynchronousSubscribersEnabled = true;
27+
});
28+
});
29+
30+
using var provider = services.BuildServiceProvider();
31+
```
32+
33+
Under the covers `AddEventFlow` calls `EventFlowOptions.New(serviceCollection)` and stores a single `EventFlowConfiguration` instance in the container as both `IEventFlowConfiguration` and `ICancellationConfiguration`. Additional fluent helpers (e.g., `.AddEvents`, `.AddCommands`, `.UsePostgreSqlEventStore`) can be chained in the same callback.
34+
35+
!!! tip
36+
You can invoke `.Configure(...)` multiple times. Each delegate receives the same `EventFlowConfiguration` instance, so later calls simply overwrite earlier values.
637

7-
EventFlow configuration can be done via the `.Configure(o => {})` method, which is available on the `EventFlowOptions` object.
38+
## `EventFlowConfiguration` reference
39+
40+
`EventFlowConfiguration` is defined in `Source/EventFlow/Configuration/EventFlowConfiguration.cs`. All properties are mutable so that they can be adjusted during startup.
41+
42+
| Setting | Default | Used by | Effect |
43+
| --- | --- | --- | --- |
44+
| `LoadReadModelEventPageSize` | `200` | `ReadModelPopulator.LoadEventsAsync` | Controls how many events are fetched per call when bulk-populating read models via `IReadModelPopulator`. Increase this if your event store can stream large pages efficiently; reduce it when replaying against constrained backends.
45+
| `PopulateReadModelEventPageSize` | `10000` | `ReadModelPopulator.ProcessEventQueueAsync` | Sets the batch size used when dispatching replayed events to read-store managers. Lower values trade throughput for lower memory pressure during large replays.
46+
| `NumberOfRetriesOnOptimisticConcurrencyExceptions` | `4` | `OptimisticConcurrencyRetryStrategy` | Upper bound on how many times the aggregate store retries commits when the persistence layer reports `OptimisticConcurrencyException`.
47+
| `DelayBeforeRetryOnOptimisticConcurrencyExceptions` | `00:00:00.100` | `OptimisticConcurrencyRetryStrategy` | Delay inserted between those retries. Combine with the retry count to soften hot spots in high-contention aggregates.
48+
| `ThrowSubscriberExceptions` | `false` | `DispatchToEventSubscribers` | When `false`, synchronous subscriber exceptions are logged and wrapped in a resilience strategy; when `true`, they are rethrown so the calling command handler observes the failure immediately.
49+
| `IsAsynchronousSubscribersEnabled` | `false` | `DomainEventPublisher.PublishToAsynchronousSubscribersAsync` | When enabled, every asynchronous subscriber invocation is scheduled through `IJobScheduler` (`InstantJobScheduler` by default). Pair this with a durable scheduler such as `EventFlow.Hangfire` to honor delayed execution.
50+
| `CancellationBoundary` | `CancellationBoundary.BeforeCommittingEvents` | `ICancellationConfiguration.Limit` | Decides how far cancellation tokens propagate through the command pipeline. Choose a later boundary if downstream infrastructure (read stores, subscribers) should respect cancellation requests.
51+
| `ForwardOptimisticConcurrencyExceptions` | `false` | `AggregateStore` | When `true`, optimistic concurrency exceptions are forwarded to `IAggregateStoreResilienceStrategy.HandleCommitFailedAsync` before bubbling out. Use this if you implement a custom resilience strategy that can translate conflicts into domain-specific outcomes.
52+
53+
!!! note
54+
The enum values for `CancellationBoundary` are defined in `Configuration/Cancellation/CancellationBoundary.cs` and progress in chronological order through the command pipeline (`BeforeUpdatingAggregate``BeforeCommittingEvents``BeforeUpdatingReadStores``BeforeNotifyingSubscribers``CancelAlways`).
55+
56+
## Practical configuration scenarios
57+
58+
### Enable durable asynchronous subscribers
859

960
```csharp
10-
using var serviceCollection = new ServiceCollection()
11-
// ...
12-
.AddEventFlow(e => e.Configure(o =>
61+
using Hangfire;
62+
using EventFlow.Hangfire.Extensions;
63+
64+
services.AddHangfire(config => config.UseInMemoryStorage());
65+
services.AddHangfireServer();
66+
67+
services.AddEventFlow(options =>
68+
{
69+
options.Configure(cfg =>
1370
{
14-
o.IsAsynchronousSubscribersEnabled = true;
15-
o.ThrowSubscriberExceptions = true;
16-
}))
17-
// ...
18-
.BuildServiceProvider();
71+
cfg.IsAsynchronousSubscribersEnabled = true;
72+
});
73+
74+
options.UseHangfireJobScheduler();
75+
});
1976
```
2077

21-
In this example, we enable asynchronous subscribers and configure EventFlow to throw exceptions for subscriber errors. You can customize the configuration options to suit your needs.
78+
Setting `IsAsynchronousSubscribersEnabled` causes `DomainEventPublisher` to enqueue a `DispatchToAsynchronousEventSubscribersJob` for every emitted domain event. Without a scheduler such as Hangfire, the bundled `InstantJobScheduler` executes jobs immediately in-process, effectively making asynchronous subscribers synchronous.
79+
80+
### Harden aggregates against hot-spot contention
81+
82+
```csharp
83+
services.AddEventFlow(options =>
84+
{
85+
options
86+
.ConfigureOptimisticConcurrencyRetry(retries: 8, delayBeforeRetry: TimeSpan.FromMilliseconds(500))
87+
.Configure(cfg => cfg.ForwardOptimisticConcurrencyExceptions = true);
88+
});
89+
```
90+
91+
The retry helper only adjusts the built-in retry strategy. Setting `ForwardOptimisticConcurrencyExceptions` allows a custom `IAggregateStoreResilienceStrategy` to inspect the conflict and, for example, emit a domain-specific execution result instead of throwing.
92+
93+
### Tune read model replay throughput
94+
95+
```csharp
96+
services.AddEventFlow(options =>
97+
{
98+
options.Configure(cfg =>
99+
{
100+
cfg.LoadReadModelEventPageSize = 1000; // event store paging
101+
cfg.PopulateReadModelEventPageSize = 2000; // read model batch size
102+
});
103+
});
104+
```
105+
106+
These knobs directly influence `IReadModelPopulator.PopulateAsync`. Smaller batches reduce memory footprint and can help when replaying to remote databases; larger batches maximize throughput when the event store and read store are co-located.
107+
108+
### Adjust cancellation semantics
109+
110+
```csharp
111+
services.AddEventFlow(options =>
112+
{
113+
options.Configure(cfg =>
114+
{
115+
cfg.CancellationBoundary = CancellationBoundary.BeforeNotifyingSubscribers;
116+
});
117+
});
118+
```
119+
120+
Raising the boundary ensures cancellation tokens are honored while rebuilding read stores, but once the boundary is crossed EventFlow will run to completion to keep the event store and read models consistent.
121+
122+
## Consuming configuration at runtime
123+
124+
Every component registered with the container can request `IEventFlowConfiguration` or `ICancellationConfiguration` to observe these values.
125+
126+
```csharp
127+
using EventFlow.Configuration;
128+
129+
public class ProjectionWorker(IEventFlowConfiguration configuration)
130+
{
131+
public Task HandleAsync(CancellationToken cancellationToken)
132+
{
133+
var maxBatchSize = configuration.PopulateReadModelEventPageSize;
134+
// ... use the configured value
135+
return Task.CompletedTask;
136+
}
137+
}
138+
```
139+
140+
This is useful when custom infrastructure (for example, an outbox publisher) needs to stay in lockstep with the same retry and cancellation semantics as the built-in components.
141+
142+
## See also
143+
144+
- [Subscribers](../basics/subscribers.md) — explains synchronous vs. asynchronous subscribers in detail.
145+
- [Queries and read stores](../basics/queries.md) and [Read store integrations](../integration/read-stores.md) — pair naturally with the read model replay settings.
146+
- [Commands](../basics/commands.md) — outlines how command handlers surface execution results that may be impacted by retry and exception settings.

0 commit comments

Comments
 (0)