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 ReadModelBuilder
which 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 FooRenamedEventHandler
renaming foo.name
, it will
call the FooReadModelBuilder
which re-writes the complete Foo
instance.
But I think this has drawbacks. The FooRenamedEventHandler can deal much
better with redundant usages of foo.name
within the read-model.
UPDATE:
Another approach could be to let the ReadModelBuilder
create read-model entities by segmenting the domain instance into events, which will build the complete read-side entity when executed sequentially.
For example:
Article
domain entity has a Name
and a Price
.
To build the read-side model, ReadModelBuilder
could inspect the domain entity and emit ArticleCreatedEvent
, ArticleRenamedEvent
and ArticlePriceChangedEvent
. 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
_
If you don't persist the event (i.e. not using Event sourcing
) then you can't easily rebuild a read-model
. Your Rebuilder
must somehow try to reverse engineer the write model and fabricate some events which is weird because the write model
could not even contain all the information as it does not need it in order to do his job.
So, my conclusion is that, without an event store
or at least an event log
then you cannot rebuild your read-model
. If you have some source of truth like this then you can rebuild it and even detect the out-of-sync situation by using a list of all processed event IDs in some persistence.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With