In DDD/CQRS, should ReadModel act as ViewModel, if not then where belongs responsibility for mapping?

cqrs, domain-driven-design

Solution

First of all, you do a wrong assertion:

...read model ProductCatalogueItem is built from aggregates/write-models...

Read model doesn't know of aggregates or anything about write model, you build the read model directly from the database, returning the data needed by the UI.

So, the view model is the read model, and it doesn't touch the write model. That's the reason why CQRS exists: for having a different model, the read model, to optimize the queries for returning the data needed by the client.

Update

I will try to explain myself better:

CQRS is simply splitting one object into two, based on the method types. There are two method types: command (any method that mutates state) and query (any method that returns a value). That's all.

When you apply this pattern to the service boundary of an application, you have a write service and a read service, and so you can scale differently the command and query handling, and you can have also two models.

But CQRS is not having two databases, is not messaging, is not eventual consistency, is not updating read model from write model, is not event sourcing. You can do CQRS wihtout them. I say this because I've seen some misconceptions in your assertions.

That said, the design of the read model is done according to what information the user wants to see in the UI, i.e., the read model is the view model, you have no mapping between them, they both are the same model. You can read about it in the references (3) and (6) bellow. I think this answer to your whole question. What I don't understand is the filtering issue.

Some good references

(1) http://codebetter.com/gregyoung/2010/02/16/cqrs-task-based-uis-event-sourcing-agh/

(2) http://www.cqrs.nu/Faq/command-query-responsibility-segregation

(3) "Implementing Domain Driven Design" book, by Vaughn Vernon. Chapter 4: Architecture, "Command-Query Responsibility Segregation, or CQRS" section

(4) https://kalele.io/really-simple-cqrs/

(5) https://martinfowler.com/bliki/CQRS.html

(6) http://udidahan.com/2009/12/09/clarified-cqrs/

Problem

Assume read model `ProductCatalogueItem` is built from aggregates/write-models, stored separately from write-models, and contains each product available for selling, and has following properties: - basics: `product_code`, `name`, `price`, `number_of_available_stock`, - documentation: `short_description`, `description`,... - product characteristics: `weight`, `length`, `depth`, `width`, `color`,... And, there are two views: - product list containing list/table/grid of available product offers, and the view needs only following basic properties: `product_code`, `name`, `price`, `number_of_available_stock`, - product details showing all the properties - basics, documentation, product characteristics. Naturally, there come two ViewModels in mind: - `ProductCatalogueListItem` containing only basic properties, - `ProductCatalogueItemDetails` containing all the properties. Now,.. there two options (I can see). - ViewModels are 1:1 representation of ReadModels Therefore the are two read models, not one, `ProductCatalogueListItem` and `ProductCatalogueItemDetails`. And, the read service will have two methods: - `List<ProductCatalogueListItem> searchProducts(FilteringOptions)`, - `ProductCatalogueItemDetails getProductDetails(product_code)`. And, controllers return these models directly (or, mapped to dto for transport layer). The issue here is filtering,.. should read service perform search query on a different read model, than is returned from the method call? Because, ProductCatalogueListItem doesn't have enough information to perform filtering. - ViewModels are another project of ReadModels The read service will have two methods: - `List<ProductCatalogueItem> searchProducts(FilteringOptions)`, - `ProductCatalogueItem getProduct(product_code)`. And, the mapping from ReadModels to ViewModels is done by upper layer (probably controller). There is no issue with filtering,... But, there is another issue, that more data leave domain layer, than is actually needed. And, controllers would grow with more logic. As there might be different controllers for different transport technologies, then mapping code would probably get duplicated in those controllers. Which approach to organize responsibilities is correct according to DDD/CQRS, or completely something else? The point is: - should I build two read models, and search using one, then return other? - should I build single read model, which is used, and then mapped to limited view to contain only base information for view?

Original source