EventFlow icon indicating copy to clipboard operation
EventFlow copied to clipboard

GetAll in EventFlow

Open alexeyfv opened this issue 11 months ago • 3 comments

Hi,

Could you suggest how to retrieve all aggregates or read models? I couldn't find any built-in functionality for this.

I understand that retrieving all aggregates is expensive in terms of performance, because EventFlow needs to replay all events for each aggregate.

However, it seems that read models also lack this functionality. I found an old issue (#550), but it was closed.

For example, If I have a UserAggregate and I want to display the list of users. How to do it?

Thanks.

alexeyfv avatar Feb 18 '25 15:02 alexeyfv

I have been thinking of doing an implementation of this, but it would require the use of e.g. IAsyncEnumerable or something similar to keep the memory usage low. I have heard of users of EventFlow having literally thousands of events per aggregate and thus memory becomes a concern. It might be that EventFlow flow could ship with a "dumb" version that has a lot of caveats, e.g. does one at a time and doesn't care about changing aggregates after the process was done.

If you want to have an easy way to do it, merely get a list of all the aggregates from the store and use the IAggregateStore to iterate though all of them. You might want to partition the data and split it across multiple works.

rasmus avatar Mar 09 '25 10:03 rasmus

.analyze

rasmus avatar Oct 26 '25 18:10 rasmus

Issue Insight

  • Aggregates are intentionally only addressable by identity; the public API on IAggregateStore exposes LoadAsync, UpdateAsync, and StoreAsync per ID with no enumeration hook (Source/EventFlow/Aggregates/IAggregateStore.cs:34). Replaying all aggregates would require enumerating the entire event store, which the framework avoids because it is expensive and backend-specific.
  • Read-store abstractions mirror that philosophy: IReadModelStore<TReadModel> only guarantees GetAsync(id), UpdateAsync, and deletion methods (Source/EventFlow/ReadStores/IReadModelStore.cs:31, Source/EventFlow/ReadStores/ReadModelStore.cs:43). There is no cross-store GetAll because each persistence technology exposes different query semantics.

Available Options Today

  • In-memory read models already support bulk queries via IInMemoryReadStore<T>.FindAsync(Predicate<>), as used by the shipping sample to list all voyages (Source/EventFlow.Examples.Shipping.Queries.InMemory/Voyage/QueryHandlers/GetAllVoyagesQueryHandler.cs:33).
  • MongoDB read models expose LINQ-style accessors (FindAsync/AsQueryable) for arbitrarily shaped queries (Source/EventFlow.MongoDB/ReadStores/MongoDbReadModelStore.cs:109).
  • SQL-backed read models deliberately omit generic query helpers; instead you are expected to hit the read-model table using the connection of your choice (e.g., through Dapper or Entity Framework) inside an IQueryHandler. The documentation stresses wiring custom queries through the query processor rather than expecting canned GetAll helpers (Documentation/basics/queries.md:73).

Suggested Approach for Listing Users

  • Project UserAggregate events into a read model (UserReadModel) stored in the backend that fits your needs (SQL table, Mongo collection, etc.).
  • Implement an IQuery<UserListResult> plus handler that uses the store-specific API to fetch all documents: FindAsync(_ => true, …) for in-memory, a Mongo filter of {} for MongoDB, or a simple SELECT * FROM Users via ISqlConnection for SQL.
  • Register the query handler with EventFlow and call it through IQueryProcessor.ProcessAsync(new GetAllUsersQuery(), cancellationToken); so the rest of the application remains decoupled from persistence details.

Why No Built-In GetAll

  • A generic GetAll across aggregates or read stores would require EventFlow to understand paging, filtering, and backend-specific query plans—capabilities it deliberately omits to stay storage-agnostic and to keep replay costs explicit.
  • The framework expects consumer code to tailor read-side projections and queries to the UX needs; this is why only “read model by ID” and “in-memory predicate” queries ship out-of-the-box.

Next Steps

  1. Decide which read-store implementation you use for users and expose an appropriate repository/query handler for bulk retrieval.
  2. If the pattern is unclear to newcomers, consider a documentation PR referencing the shipping example to demonstrate the recommended approach for list views.

Your friendly neighborhood AI

github-actions[bot] avatar Oct 26 '25 18:10 github-actions[bot]