3

我目前正在设计一个系统,该系统将使用多个数据源来使用所需的数据。我正在尝试对下面显示的概念进行建模(将发布图像但没有足够的分数!)客户可以与许多产品相关联。客户将存储在“客户子系统”中,产品和客户产品将存储在您的“产品子系统”中

public class Customer
{
    public string FirstName { get; set; }

    public Guid ID { get; set; }
}
public class CustomerProduct
{
    public Guid ID { get; set; }

    public Customer Customer { get; set; }

    public Product Product { get; set; }
}
public class Product
{
    public string Name { get; set; }

    public double Price { get; set; }

    public Guid ID { get; set; }
}

“客户”实体将物理保存在必须通过 Web 服务访问的系统中。“ConsumerProduct”和“Product”实体将保存在 SQL 数据库中,使用 NHibernate 作为 ORM。

作为设计的一部分,我计划使用存储库将数据持久性技术从域模型中抽象出来。因此,我将有 3 个存储库接口,ICustomerRepository、ICustomerProductRepository 和 IProductRepository。然后,我将为 CustomerProduct 和 Product 存储库创建一个具体的 NHibernate 实现,并为 Customer 存储库创建一个具体的 Web 服务访问实现。

我正在努力解决的是持久存在于不同子系统中的实体将如何交互。理想情况下,我想要一个丰富的域模型,其中 CustomerProduct 实体将具有一个返回 Customer 对象的物理“Customer”属性。但是我不知道这将如何工作,因为需要从不同的数据存储访问 Customer 实体。

我能看到解决此问题的唯一方法是不在 CustomerProduct 实体中维护对 Customer 的完整引用,而是只保留一个引用,然后每次我需要获取对 Customer 的引用时,我都会通过 Customer存储库。

如果有人能就如何解决这个问题提出任何建议,我将不胜感激。

4

1 回答 1

3

嗨,我以前没有遇到过你的情况,但我设计了与其他子系统通信的域。我没有全貌,但似乎客户实体与其他实体(CustomerProduct 和 Product)更加孤立。那么我猜你将在一个通用的 GUI 中呈现模型但它只是分离的数据源是正确的吗?

首先,您可以通过不同的方式解决这个问题,您还应该问自己一些非功能性要求,例如维护、正常运行时间和支持。两个系统会始终同时启动并运行,还是会发生您关闭系统的情况。我正在寻找的线索是您应该与子系统通信同步还是异步(消息队列?)。这可以通过使用 NServiceBus 来实现。

但是要专注于您的域,您应该使域看起来只有一个模型。这可以通过不同的方式实现:

1) 让您的 ICustomerRepository(一个接口契约,其作用就像是针对一组对象工作)由一个与基础设施相关的存储库实现,该存储库在您的子系统中使用您的 Web 服务。一个提示是您应该使用 GUID 作为键,以便发生键冲突。这种方法不会让您与其他实体的客户建立任何关系/关联。他们只会通过存储库(这是 Jimmy Nilsson 在他的书 ( http://jimmynilsson.com/blog/ ) 中使用的一种解决方案,用于不将模型与许多双向关系收紧)。

2) 取决于您的用例将如何定位/使用模型,但您可以创建一个应用程序范围的服务层,该服务层位于一个物理位置,但使用 CustomerService 和 CustomerProcuctService 和 ProductService。为了防止域逻辑泄漏到应用层,这些实体之间的一些协调可以封装在域事件处理程序中,这些事件处理程序协调不同服务之间的一些事件。

3)您还可以创建一个 CustomerAdapter 类,该类将其他子系统 CustomerGUID 作为键(它不能生成键,因为客户 Web 服务可以控制它)。但是您可以在 Nhibernate 中映射它并在 CustomerProduct 和 CustomerAdapter 之间建立关系。但是,当您映射 CustomerAdapter 时,您只会加载 GUID。然后确保您将使用 Spring.Net Windsor 或其他一些 DI 工具将 ICustomerAdapterService 注入到属性中。然后你不要在 Nhibernate 中为 customerAdapter 映射属性(如客户名称、地址等)。但是,当您从 CustomerAdapter 获取/读取 Adress 时,它将从 ICustomerAdapterService 获取它并设置所有其他值。这不是推荐的解决方案,因为它会破坏一些 DDD 规则,例如在域模型中没有服务。但如果你从这个角度来看:它实际上可以被视为域服务,因为它解决了您的分布式域中的问题。但是,它包括与基础架构相关的内容,例如 WCF 服务实现,因此服务实现是否应该在另一个基础架构层/组件中

最简单的是解决方案 2 如果您可以处理客户实体只能由应用程序层中的服务访问的事实。然而,这个应用服务层可以是两个子系统之间的一个很好的反腐败层。您今天有两个子系统可能是有原因的。但是一个交互流的例子(没有你的领域的详细知识):

  • GUI 调用 Application Service CustomerProductService 方法 BuyNewProduct(CustomerDTO customer, ProductDTO newProduct)
  • CustomerProductService 将 ICustomerProductRepository 和 IProductRepository 注入到构造函数中。它还将有一个基础结构服务 ICustomerFacadeService(现在更改名称 :-)),它被注入到属性 CustomerFacadeService。该服务的创建由具有两种创建方法 Create() 和 CreateExtendendedWithCustomerService() 的工厂完成。后面的也会注入 CustomerServiceFacade
  • BuyNewProduct(...) 方法现在将组装 CustomerDTO 并使用 CustomerGUID 从 CustomerFacadeService 加载 Customer,该服务将调用另一个子系统中的 Web 服务。
  • 加载的客户将确保它确实存在,但现在我们使用 IProductRepository 加载产品
  • 使用 CustomerGUID 值和产品实体,我们创建一个新的 CustomerProduct 实体(实际上只是产品和客户 GUID 之间的映射类)并通过 ICustomerProductRepository 保存它
  • 现在,您可以调用另一个基础架构服务向您的客户发送一封电子邮件,该客户将收到通知它可以访问新产品。或者,您可以在 CustomerProduct 实体中创建域事件,该实体将此通知委托给在 ctor 中注入 IEmailService 的事件处理程序(在应用程序服务层中)。然后,当您将新客户连接到产品时,您已经掌握了发送通知的领域知识。

希望这可以帮助您以更少的痛苦为您的域建模。因为做 DDD 很痛苦。需要在镜子前与同事、领域专家和您自己进行大量讨论 :) 这是正确的道路吗?在 DDDsample.net 上查看域事件或搜索 Udi Dahan 和域事件。


我在这里写一个答案,更多空间:

关于 CustomerAdadpter 在交互流示例中也称为 CustomerFacadeService,我认为:如何实现取决于您的应用程序。大多数用户案例是否会调用 mainsystem 调用您的“云子系统”,这将具有良好的正常运行时间:-那么您可能不需要队列并且将在云中拥有 WCF 服务。您的 CustomerFacadeService 将是一个服务包装器,它仅公开您的应用程序层所需的方法,并组装所有必要的 DTO 对象。如果您的云系统也将回调到您的主系统,那么您需要将您的一些方法公开为服务。那么您可以选择将 NServiceBus 端点公开为 WCF 服务。这使您可以在不丢失信息的情况下关闭主系统。但是总是有很多问题... 如果您的基础设施技术人员想要安装修补程序/重新启动主系统的 Web 服务器,您当然需要在另一台机器上安装 WCF 服务。如果您在主系统关闭时让客户等待响应,他们将等待多长时间?估计不会太久。因此,我可以看到这样做的一种情况是,如果您有需要执行的批次/报告,并且如果系统的一部分已关闭,则报告将在它再次启动时继续。

这是作为 WCF 服务公开的 NServiceBus 的一些示例。但是我没有这样做的经验,只是“可以做到”的知识。 http://docs.particular.net/nservicebus/architecture/nservicebus-and-wcf

于 2011-04-07T11:47:21.213 回答