将CQRS与存储库一起使用

时间:2017-10-25 07:27:40

标签: c# asp.net-web-api cqrs

如果我理解正确CQRS是关于划分写和读责任。所以我可以在我的写模型中使用存储库,例如var user = repository.GetUserById(); - 这将通过id获取用户,然后repository.UpdateUser(user);将使用更改的属性更新用户。在阅读模型中,我们可以构建更复杂的DTO:

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;
        }

    }

所以在我的Write模型中,我从我的存储库中获取id,因为我不需要将它映射到DTO,在我的读模型中我使用GetUser by id,但是我将它映射到DTO,因为我以这种方式需要它。不是这个代码重复(如果我想改变获取用户的ID我将不得不在两个地方改变它)?我可以在阅读模型中使用存储库吗?在这种情况下,我将不得不在UsersReadModel中使用存储库和上下文(对于匿名对象,并选择表列的一部分)。

3 个答案:

答案 0 :(得分:2)

如果您的域非常简单,那么Write和Read将非常相似,并且会发生大量的cod重复。实际上,这也是相反的,如果您的Write模型与Read模型非常相似,那么您可以将它们实现为CRUD,并且您不必 需要CQRS。

  

我可以在阅读模型中使用存储库吗?

您可以在阅读方面拥有所需的任何;双方从许多观点分开。

在CQRS中,发生代码重复的情况很多。不要害怕。您可以在共享类中提取它。

P.S。 你应该为每个用例都有一个Read模型,而不是每个Write模型。如果你有一个从Write到Read的1:1对应关系,那么这个可以也意味着你应该使用CRUD实现这一点。

P.S。我喜欢使用CQRS,即使域很简单,因为我喜欢非常优化的Read模型(不同的持久性类型,没有JOINS,自定义数据分片等)。

答案 1 :(得分:2)

这里有几点需要注意。根据您的描述,听起来并不像读写模型之间存在分离。请记住,它们的目的非常不同。

CQRS严重依赖域驱动的设计原则。关键原则是封装域对象。

因此,您不会期望域对象具有&#39;属性&#39;在它上面(特别是不是制定者)。它可能有ID,但不是很多。这是因为它的作用是保护自身内的不变量。如果你有制定者,那么你不能轻易做到。

我还认为域名对象除了id之外不应该具有getter。如果你有一个很好的阅读模型,几乎不需要它们,并可能鼓励不正确地使用该对象。有时候这个想法可以放松一点。虽然我现在无法想到这一点。

因此,域对象的存储库可以非常简单。 GetById和Save(除非您使用的是事件采购,但这是另一个主题)。

另一方面,Read模型应该被塑造为UI。每种模型都可能混合各种来源的数据。例如,您可能希望在上下文或其活动或订单或公司价值或应用程序的目的中查看用户详细信息。

对CQRS应用程序典型结构的这种解释可能会有所帮助:CQRS + Event Sourcing - A Step by Step Overview

这可能会让您深入了解如何创建域对象:Aggregate Root - How to Build One for CQRS and Event Sourcing

希望这有帮助。

答案 2 :(得分:1)

  

如果我理解正确CQRS是关于划分写和读责任。

更接近的是,它是关于拥有专为其支持的用例而设计的数据模型。

我们有真理,这个真理有多种表现形式。

诀窍在于表达不需要及时耦合 - 我们可以更新记录和#34;表示现在,以及我们用来支持查询的表示最终

  

我可以在阅读模型中使用存储库吗?

绝对。没有魔力。

Udi Dahan可能会建议您考虑different repositories,或者更精确地考虑您的存储库中的方法,这些方法根据您正在执行的操作提供读取模型的不同显式表示。每个方法都会加载特定用例所需的表示。