Sharing a base model is against everything that CQRS
stands for.
If your models (write and read) are 99% percent then it is a CRUD
architecture painted to look like CQRS
.
You must split the write from the read because the two models have different behaviors: the write model is concerned with ensuring the invariants and the read model is concerned with the reading and displaying the data to the user. It is normal that some of the fields are shared but only as concepts. In the write side, you create Commands
that have those fields, send them to the write models (Aggregates
in DDD
) and generate events
that have some of the fields from the commands
. In the read side you read the events
and create some simple persistence objects, with no behavior that ensure the invariants; they can have behavior but a different kind from the write models.
On the read side you could have mega objects that contain fields from multiple write models. The main idea of the read models is to be very fast (i.e. no JOINS
), so you include in a row
or document
all you need to display the data to the users.
On the other side, the write side, you put in commands
only the minimum information needed to maintain the invariants
(i.e. you refer other Aggregate roots only by their IDs).
So, as a metaphor, the write and read models are like matter and antimatter: they should not collide; they are separated (decoupled) by events that act like a magnetic fields.
UPDATE:
The read models only see the events generated by the Aggregates (the write models); they don't query the write models as it is forbidden; so the write models have only command methods
and no query methods
; on the other hand, the read models have only query methods
(i.e. getters
) and no command methods
; the read models are immutable; the only way to mutate
the read models is by listening to the events
and apply those events
them.