The common name is ‘projection’ and I guess a Projector is what creates them (e.g. for a specific domain aggregate). You will get a valid instance of the aggregate by hydrating it with all the Events that occurred either from the time of its creation, or - e.g. in case the number of events is too large for this - going from a Snapshot of the aggregate’s state at a certain moment in time, and applying Events from there.
Didn’t give this much thought, but there’s no 1-to-1 mapping conceptually to the event sourcing paradigm with regards to using ActivityStreams Objects + Activities as the events themselves. In ES an event only needs to contain the state changes, and some metadata (like an
aggregateId). Many things in AP sent over the wire contain much more than just this state, and may contain nested objects/activities.
The match is still a very good one. I would go from a CQRS/ES architecture where incoming messages on the C2S/S2S api’s trigger commands that are executed, e.g.
FavoriteToot. A successful follow request then triggers a
PersonFollowed event on the
Person actor (an aggregate root in DDD terminology).
But I think @berkes has given this some thought already when mentioning ‘internal events’. There needs to be some translation from e.g. an incoming ‘Like’ activity to a ‘Liked’ event applied to a specific ‘Note’ or ‘Actor’ or whatever aggregate root, which is subsequently persisted in the event store. One incoming AP message may trigger multiple events. Besides executing separate commands, the events may trigger sagas (workflows) that invoke other commands in turn.
Note that CQRS and ES are different concepts and can be implemented independently of each other. CQRS means separating the ‘reads’ from the writes’, typically by having Command classes (writes) and Query classes (reads). Without Event Sourcing executing a command might lead to persisting data in a normalized relational DB model, and - when querying - consulting denormalized views that are optimized for quickly loading specific UI layouts. But that last bit is not required either.
With ES in the mix, you could still do with just one DB. With CQRS/ES you get things like:
CreateUserCommand (a use case / feature) ➜
UserCreatedEvent. When fully separating write-side and read-side and having 2 databases things get most interesting, but also most complex. When storing events in a single table, or a specialized eventstore, you can now reproduce the state of the system in any moment of time, do time-travel, etc. Plus no data gets deleted, whereas in a relational CRUD system with every update you lose history.
Though it has a lot of advantages, the cons - apart from deletion being harder - are also added complexity due to eventual consistency, where the state of the read side lags behind that of the write side. It can be harder to trace what is happening in your system e.g. when this creates timing-related issues.
Btw, am no expert either, but very interested in a DDD / CQRS and maybe ES and maybe Actor Model (yes, all the buzzwords ) based architecture for a fediverse application. Note that for testing behavior-driven design (BDD) is very well suited, and you can have executable tests based on plaintext feature descriptions (very nice to get non-technical people in the loop).
I am looking to implement with @cjs go-fed due to the solid AS/AP foundation it delivers, and how it supports modularity and extendability where AP extensions are defined in a JSON-LD-formatted OWL2-subset vocabulary definition. (I am no Go programmer yet, so that’s a challenge).
Finally, what’s also interesting if you go DDD/CQRS/ES is to apply Clean Architecture in your project structure, i.e. browsing the code repo should immediately make clear which file contains what. It communicates the architecture. I just finished a follow-up to a discussion I have about this. See Clean architecture folder structure on github.
@berkes what kind of app do you have in mind? And what language / frameworks do you want to use?