Let's say you want to implement a blog platform with the following requirements:
Create a new draft article
Update a draft article (update text/add images/change author etc etc)
Publish a draft article (record a revision number for each published article)
Rollback state of draft to a previous revision
Does it make sense to take advantage of the fact that we have events for each version of our article to implement the Publish / Rollback functionality? For example:
ArticleCreated
ArticleBodyUpdated
ArticleImageUpdated
ArticleAuthorUpdated
ArticlePublished (Revision 1 created, view model is updated to reflect published article)
ArticleBodyUpdated
ArticleImageUpdated
ArticleAuthorUpdated
ArticlePublished (Revision 2 created)
ArticleBodyUpdated
ArticleImageUpdated
ArticleAuthorUpdated
ArticleRolledBack (Rollback to revision 1)
ArticleBodyUpdated
ArticleImageUpdated
ArticleAuthorUpdated
ArticlePublished (Revision 3 created)
When loading my Article aggregate from events, I would want it to contain the state represented by the most recent draft updates, minus any changes that were rolled back. Normally to implement undo functionality, I would use compensating events but in this case, there could be hundreds of different types of events that can modify the state of my aggregate (in this example, I've only shown 3 but imagine there are others). In seems wrong to create so many compensating events to implement a single feature.
It seems that I need to separate out the draft state from the published state. Here is an outline of how I believe the aggregate should look
class Article {
ArticleState draftArticle;
ArticleState publishedArticle;
int publishedRevision;
}
However, I cannot figure out how to maintain these article states given the sequence of events above without rebuilding a new ArticleState from scratch every time I encounter an ArticleRolledBack event. For articles that contain lots of rollbacks and revisions, this would quickly become massively inefficient.
Is there a better pattern I can use for building the state of my aggregate from a subset of previous events? Is this approach completely wrong for implementing this requirement? What approach would you use?
Alex