Domain driven design 从积垢到DDD

Domain driven design 从积垢到DDD,domain-driven-design,Domain Driven Design,我有一个管理门票和客户的应用程序。一位顾客拥有许多票。如果客户被删除,其票据也会被删除。这是查看对象是否应为聚合的测试之一。我选择它们都应该是不在聚合根下的实体。我通常会加载并显示跨越许多客户的门票列表。我正在使用SQL Server以关系格式保存数据 我的架构如下所示: ASP.NETMVC应用程序 WCF服务(门面)。此服务具有公共方法,例如GetTicket、GetTickets、GetCustomer和GetCustomers。为此,它直接使用存储库。它还有一个名为Run的公共方法。Ru

我有一个管理门票和客户的应用程序。一位顾客拥有许多票。如果客户被删除,其票据也会被删除。这是查看对象是否应为聚合的测试之一。我选择它们都应该是不在聚合根下的实体。我通常会加载并显示跨越许多客户的门票列表。我正在使用SQL Server以关系格式保存数据

我的架构如下所示:

  • ASP.NETMVC应用程序
  • WCF服务(门面)。此服务具有公共方法,例如GetTicket、GetTickets、GetCustomer和GetCustomers。为此,它直接使用存储库。它还有一个名为Run的公共方法。Run接受一个命令,该命令可以是AssignTicket、ChangeTicketName等。为了处理这些命令,facade注入了服务、ITicketService和ICCustomerService。这些服务上的方法用于执行facade中的命令。这些服务使用ITicketRepository和ICCustomerRepository加载数据
  • 关系型SQL Server数据库
  • 我正忙于维护从门票到顾客的参考资料。与此相关,票证具有映射到Customers表的FK。当UI查看单个票据时,它调用Facade.GetTicket(id)和Facade.GetCustomers()。我能够显示所有的票证详细信息以及它所属的客户名称。一切都很好。但是,如果有很多不同客户拥有的一大串门票呢。请记住,票据仅包含引用客户的Guid。我不想通过调用facade来获取我将在UI中列出的每一张票的客户名称。我的票证上有一个名为CustomerInfo(CustomerId和CustomerName)的值对象是否有效?使用sql语句将此数据加入是否有效?如果我没有使用关系数据库呢?当客户的名字被更改时会怎样?在系统中,票证实际上也包含对多个其他实体的引用

    似乎DDD业务逻辑和UI需要显示的内容之间存在着断开连接

    如果客户被删除,其票据也会被删除。这是查看对象是否应为聚合的测试之一

    我不希望你的领域专家说他们“删除客户”。客户可能被禁止,或者他们的帐户被暂停,门票可能被取消或转让,但是“删除”这个词非常以CRUD为中心。理想情况下,您不应该真的硬删除数据(也许您可以将其归档到另一个数据存储中),IMHO级联删除是一个危险的举动。让你的ORM来处理

    定义聚合就是定义一个“一致性边界”,根据领域专家为该聚合定义的不变量,在该边界内聚合的状态是“正确的”。这就是定义聚合边界的方式

    我一直在忙着维护从门票到顾客的推荐信


    要清楚的是,域模型(其中实体和值对象被组装成聚合体)和数据模型(它实际上只是所选OOP语言中数据库的代码表示)之间有区别。您的数据模型就是您的表,可能与您的域模型完全不同。数据模型可以在不存在实际引用的表之间建立关系。换句话说,可以跨聚合边界使用外键引用。这只是为了在数据库中强制执行引用完整性。如果要使用ORM将这些表映射到类,则不会有“导航属性”,只有ID值

    似乎DDD业务逻辑和UI需要显示的内容之间存在着断开连接

    现在我们开始讨论CQR。DDD是关于在您的问题域中建模行为的。一个域模型应该有很多行为(并且可能没有公共状态,我将继续介绍)。在调用域逻辑时,您应该在一个事务中加载聚合,调用所需的行为并保存结果。因此,您的存储库应该如下所示:

    public interface IRepository<TEntity>
    {
        TEntity Get(Guid id);
        void Save(TEntity item);
    }
    
    公共接口IRepository
    {
    TEntity-Get(Guid-id);
    无效保存(紧急项);
    }
    
    实现总是急切地加载所有内容,因为在ORM映射中,您不会在聚合之外包含对实体的引用。事实上,您现在可能已经认为文档数据库更适合存储聚合,我想您是对的

    但是你怎么去质疑呢?容易的。写一个查询。如果您正在实现CQR,那么您有一个薄的读取层,不使用存储库,不使用精心编制的实体及其ORM映射。只需编写一个查询。如果使用SQLServer,请考虑使用超快速LINQ到SQL数据上下文,或者使用Doper或Simul.DATA。甚至只是ADO.NET。关键是,您的查询是关于从数据库中获取一些数据,而不需要行为

    如果您使用文档数据库进行聚合持久化,那么CQRS模式的合理实现可能会看到您可能使用从域行为生成的事件构建一个完全独立的数据库。“读取存储”的选项是无穷无尽的。以下是一些想法:

    • 相同或其他文档数据库实例,但具有预构建的viewmodels
    • SQL数据库(您可以放弃第三种标准形式并使用复制,因为您希望优化读取性能而不是OLTP性能)
    • 磁盘上预写的HTML文件
    奖金聊天(活动来源)

    假设您使用域中的事件来构建一个读取存储区,您可以在应用程序中使用一个精简的读取层来访问该存储区(不用担心存储库或大型ORM框架)。