Skip to content

Commit 8941b23

Browse files
Merge pull request #23 from JasperFx/docs/enrichment-limitation
Document enrichment limitation with FetchForWriting/FetchLatest
2 parents 97b0003 + c97f1d7 commit 8941b23

1 file changed

Lines changed: 57 additions & 0 deletions

File tree

docs/events/projections/event-projections.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,60 @@ public void Project(IEvent<UserDeactivated> @event, IDocumentOperations ops)
6868
.Set(x => x.IsActive, false);
6969
}
7070
```
71+
72+
## Event Enrichment
73+
74+
`EventProjection` supports an `EnrichEventsAsync` hook that runs **before** individual events
75+
are processed. This allows you to batch-load reference data from the database and enrich events
76+
with it, avoiding N+1 query problems.
77+
78+
::: warning
79+
Event enrichment is designed for **read model / query model** projections processed by the async
80+
daemon or inline during `SaveChangesAsync`. It is **not** called during `FetchForWriting()` or
81+
`FetchLatest()`. Avoid depending on enriched data in write model aggregates used with those APIs.
82+
:::
83+
84+
Override `EnrichEventsAsync` in your `EventProjection` subclass:
85+
86+
```cs
87+
public class TaskSummaryProjection : EventProjection
88+
{
89+
public TaskSummaryProjection()
90+
{
91+
Project<TaskAssigned>((e, ops) =>
92+
{
93+
ops.Store(new TaskSummary
94+
{
95+
Id = e.TaskId,
96+
AssignedUserName = e.UserName // Set by enrichment
97+
});
98+
});
99+
}
100+
101+
public override async Task EnrichEventsAsync(
102+
IQuerySession querySession,
103+
IReadOnlyList<IEvent> events,
104+
CancellationToken cancellation)
105+
{
106+
var assigned = events.OfType<IEvent<TaskAssigned>>().ToArray();
107+
if (assigned.Length == 0) return;
108+
109+
var userIds = assigned.Select(e => e.Data.UserId).Distinct().ToArray();
110+
111+
foreach (var userId in userIds)
112+
{
113+
var user = await querySession.LoadAsync<User>(userId, cancellation);
114+
if (user != null)
115+
{
116+
foreach (var e in assigned.Where(a => a.Data.UserId == userId))
117+
{
118+
e.Data.UserName = user.Name;
119+
}
120+
}
121+
}
122+
}
123+
}
124+
```
125+
126+
The method is called once per tenant batch before any `Project<T>` handlers run. Modifications
127+
to event data properties are visible to all subsequent handlers in the batch.

0 commit comments

Comments
 (0)