We have built a CQRS-based system using a relational DB on the domain-side and a NoSQL DB on the read-side. The domain-side follows a classical, relational approach while the read-side is denormalized. Data replication and transformation is done using events emitted by command handlers.
I have two questions regarding read-side synchronization:
What is the best way to completely rebuild the Read Model using the relational data on the domain-side?
Let's assume the Read Model is out of sync. But even if it is always in sync, one may want to import a test database or do some bulk operations. So one may want to run the system from an existing write-model, without having the corresponding, synchronized read-model. As we do not use Event Sourcing, there is no way to replay all events.
I currently consider a
ReadModelBuilderwhich basically does a SELECT * FROM on each table and convert each entity to read-side representation. But this introduces redundancy. ReadModelBuilder would need to know how the transformation is done. So do the Event Handlers which normally do read-side synchronization after a Command Handler executed some write operations.I thought about discarding the Event Handlers and replace them with a synchronization mechanism on a per-class-level. E.g. instead of
FooRenamedEventHandlerrenamingfoo.name, it will call theFooReadModelBuilderwhich re-writes the completeFooinstance. But I think this has drawbacks. The FooRenamedEventHandler can deal much better with redundant usages offoo.namewithin the read-model.UPDATE: Another approach could be to let the
ReadModelBuildercreate read-model entities by segmenting the domain instance into events, which will build the complete read-side entity when executed sequentially. For example:Articledomain entity has aNameand aPrice. To build the read-side model,ReadModelBuildercould inspect the domain entity and emitArticleCreatedEvent,ArticleRenamedEventandArticlePriceChangedEvent. That way, the transformation logic would stay within the event handlers but would still be callable from some kind of bulk replication mechanism.For example the ReadModelBuilders could look like this:
_
interface IReadModelBuilder<TEntity>
{
//// Returns a sequence of events which replicate the read-model
//// when executed by the event handlers.
Event[] GetReplicationSequence(TEntity instance);
}
END OF UPDATE
_
- How do you generally detect a read model which is out of sync? Are there general best practices?