Gamaboy
Gamaboy

Reputation: 117

Using CQRS with repositories

If I understand correctly CQRS is about dividing write and read responsibilities. So I can use repositories in my write model, for example var user = repository.GetUserById(); - this will get the user by id and then repository.UpdateUser(user); will update the user with changed properties. In the read model we can construct more complex DTO's:

public class UsersReadModel
    {
        private IMyContext context;
        public UsersReadModel(IMyContext context)
        {
            this.context = context;
        }

        public ComplexUserDTO GetComplexUser(ISelectQuery query)
        {
            ComplexUserDTO user = new ComplexUserDTO();

            // get all user properties, GetUser by id
            user.UserDTO = context.Users.Where(d => d.UserId == query.UserId).ProjectTo<UserDTO>().FirstOrDefault(); 

            //here I don't need everything from PoliciesTable, I just need two columns, so I use anonymous object
            var policieObject = context.Policies.Where(f => f.BasePolicyId == query.PolicyId).Select(s => new { s.PoliciesNames, s.Clients.Select(d => d.ClientNames).ToList() }).FirstOrDefault();

            user.PoliciesNames = policieObject.PoliciesNames;
            user.ClientsNames = policieObject.ClientsNames;
            return user;
        }

    }

So in my Write model, I get user by id from my repository, because i don't need to map it to DTO, and in my read model I use GetUser by id, but I map it to DTO, because I need it in that way. Isn't this code repeat(if I want to change getting user by id i'll have to change it in both places)? Can I use repositories in my read model? In this case I'll have to use both repositories and context(for the anonymous object, and selecting part of table columns) in UsersReadModel.

Upvotes: 1

Views: 2940

Answers (3)

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57327

If I understand correctly CQRS is about dividing write and read responsibilities.

Closer to say that it is about having data models that are designed for the use cases that they support.

We have Truth, and that truth has multiple representations.

The trick is that the representations don't need to be coupled in time -- we can update the "book of record" representation now, and the representations we use to support queries eventually.

Can I use repositories in my read model?

Absolutely. There's no magic.

Udi Dahan would probably suggest that you be thinking about different repositories, or perhaps more precisely methods on your repositories that provide different explicit representations of the read model depending on what you are doing. Each method loads the representation that you need for that particular use case.

Upvotes: 1

Codescribler
Codescribler

Reputation: 2305

There are a few things to look at here. From your description, it doesn't sound like there is a separation between the read and write models. Remember, they have very different purposes.

CQRS leans heavily on domain-driven design principles. A key principle is the encapsulation of your domain objects.

As a result, you wouldn't expect a domain object to have 'properties' on it (especially not setters). It may have ID for example but not much else. This is becuase it's role is to protect invariants within its self. Not something you can do easily if you have setters.

I would also argue that a domain object shouldn't really have getters except for id. If you have a good read model there is little need for them and may encourage incorrect use of the object. There are times when this idea can be relaxed a little. Although I can't think of one right now.

As a result, a repository for a domain object can be very simple. GetById and Save (unless you are using event sourcing but that's another topic).

The Read model, on the other hand, should be shaped to serve the UI. Each model is likely to have a mix of data from various sources. For example, you are likely to want to see a users details in context or their activities or orders or value to the company or whatever the purpose of the application is.

This explanation of the typical structure of a CQRS application may be helpful: CQRS + Event Sourcing - A Step by Step Overview

And this may give you some insight into creating domain objects: Aggregate Root - How to Build One for CQRS and Event Sourcing

Hope this helps.

Upvotes: 2

Constantin Galbenu
Constantin Galbenu

Reputation: 17693

If your domain is very simple then the Write and the Read will be very similar and a lot of cod duplication will occur. In fact, this works in reverse as well, if your Write model is very similar to the Read model then you could implement them as CRUD and you don't necessarily need CQRS.

Can I use repositories in my read model?

You can have anything you want on the Read side; the two sides are separated from many points of view.

In CQRS there are many cases when code duplication occurs. Don't be afraid of that. You could extract that in shared classes.

P.S. You should have a Read model for every use case, not for every Write model. If you have a 1:1 correspondence from Write to Read then this could also means that you should have implemented this using CRUD.

P.S. I like to use CQRS even if the domain is simple as I like to have very optimized Read models (different persistence type, no JOINS, custom data sharding etc).

Upvotes: 2

Related Questions