I have a microservice-based application running on AWS Lambda. Two of the microservices, the most crucial ones, use event-sourcing/cqrs.
Background: (this is also for me to organize my thoughts)
I'm using this library and storing events in DynamoDB and projections in AWS S3.
The write part works like a charm: Each command invocation loads the current state of the aggregate from DynamoDB (by running events through a handler and/or loading an cached aggregate), it decides to accept or reject the command based on some business logic, then writes to DynamoDB with KeyConditionExpression: 'aggregateId = :a AND version >= :v'
where the version is a count of events processed for that aggregate. If there's a conflict, the write fails. Seems like a good system to me!
Each event is then broadcast to SNS (topic name is the service name) so other services can react to the event, if they want.
The part that I really struggle with is the read. Projections are stored in S3 and tagged with the last commitId processed for each event source. When a read query comes in, it loads the entire projected state from S3 (for all aggregates), queries the event sources for all newer events, computes the latest state (again, for all aggregates - and writing an updated object to S3 if it's newer), and returns relevant parts of the state based on the query params.
My problem: (or one of them)
I think I'm doing projections wrong.
Most of my projections only group ids by important attribute, so the files stay relatively small. But I also need a way to retrieve an individual aggregate. Using projections for that seems crazy, because I need to load the entire state each time (i.e. every projected aggregate) apply new events to that, then retrieve the record I want (it may not have even changed).
This is what I'm doing now, it's performing fine (<100k records) but I can't imagine it will continue much longer.
The other problem is queries. I need to build a projection mapping value to matching aggregateIds for every attribute I need to query on!! There's got to be a better way!
No matter what way I think about this problem, projections always need the entire current state + any new events before it can return even a single record that hasn't changed.