2

我最近开始了一个使用现有数据库(Oracle)和 MVC 4 的项目。已经发生了很多编码......但代码中没有“策略”......只有 DB -> ORM -> 控制器。因此,我正在尝试为开发添加一些闪光,并练习一些 DDD 开发技术。

我已经定义了几个聚合根,每个都有一个存储库,用于处理保存和删除(及其子)等。这些聚合根中的一个引用了另一个聚合根,它通过它处理它的“子对象”它。

例子:

A Client has one or more purchase orders which has Line Items, 
if a client wants to add a line item to the purchase order, 
it has to go through the purchase order.

很好.. 客户 Aggregate Root,采购订单 Aggregate Root。

现在,一些服务也出现了,比如修改采购订单状态的服务,它消除了采购订单AR的负担,而且它很好、干净、有目的(它可以被其他“东西”用来更新采购订单的状态),(也许这应该是采购订单 AR 的一部分?一个小细节..)

存储库目前正在做他们的工作,即从数据库中持久化数据并用数据“填充” AR。当AR “保存”它时,存储库会保存任何需要保存的内容。客户AR使用采购订单存储库,因此客户可以加载它可能包含的任何采购订单。希望我走在正确的轨道上。

现在,进入MVC。所以我也有一些 ViewModel,它们基本上是关于需要向用户输出的内容的显示定义。Automapper 已经证明 frickin AWESOME,所以我可以“自动映射”到视图模型。不需要大脑,完美..

现在,真正让我失望的实施细节..

控制器目前正在通过一个客户端工厂工作,它返回一个客户端AR,然后它可以执行采购订单列表控制器需要做的任何事情,即管理相关的采购订单(作为一个整体,而不是采购订单的详细信息或本例中的数据)。

所以现在我想确保这是正确的..因为我看到的很多例子都有控制器与存储库而不是工厂一起工作,但我也看到工厂被推荐用于创建AR ..这让我相信在示例中,控制器正在处理聚合根,但是需要“消费者”的这种方式必须查询AR才能获取AR

像:

Get the Client Aggregate where the ClientID is 15

或者更好:

get the Client Aggregate, where the ClientID is 15 
and where active purchase orders > 0 ...or something..

这可能看起来像:

ClientAR = ClientRepository.GetClientByIDAndHasActivePurchaseOrders(15);
resultsImLookingFor = ClientAR.PurchaseOrders(); //or something

我觉得在这种情况下,存储库会根据我的需要“填充”ClientAR,所以现在我有了这个 ClientAR 东西,它根据它的使用情况而有所不同,它对我来说很臭……

使用工厂“感觉更好”,因为我只是从工厂创建一个 ClientAR 然后使用它,它不会根据情况而改变.. 它就是这样..

ClientAR  = ClientFactory.CreateClient(15) // returns a ClientAR 
resultsImLookingFor = ClientAR.GetPurchaseOrdersByStatus(statusID);

或者也许我完全想念它,我应该这样做::

ClientAR = ClientRepository.GetClientByID(15, PurchaseOrderSpec)

我错过了事物的规范方面吗?(在这一点上,我还没有足够的能力真正开始使用规范(只是还),因为我需要让这些东西正常工作)

我尽量不陷入实施的细节,因为我的老板当然不会对我是怎么做的..到目前为止,至少在考虑 DDD 的情况下实施事情(希望我得到它)已经证明非常好的可测试性和责任的逻辑“边界”,这是由这种“模式”或“思维方式”产生的,我想我应该说..

那么,我是否正确地接近这个?如果它是有争议的,我可以接受。如果它只是简单的错误,那么指导肯定是值得赞赏的,如果我离得很远,如果背后有充分的理由,建设性的批评不会伤害我的感情。

提前致谢。

4

1 回答 1

8

因此,我正在尝试为开发添加一些闪光,并练习一些 DDD 开发技术。

Flare 通常不是项目的理想品质,并且可能适得其反,导致首字母缩写词驱动开发、简历驱动开发等。DDD 也是如此 - 不要在不了解缺点的情况下尝试应用 DDD 战术模式。

这些聚合根之一引用了另一个聚合根

AR 之间的引用应仅限于身份引用,而不是尽可能地引用对象。AR 应该定义一个一致性边界,该边界不一定会导致模型以最自然的方式反映现实。可以通过身份引用来表达客户和 PO 之间的关系 - PO 上有一个客户 ID。查看Vaughn Vernon 的 Effective Aggregate Design了解更多信息。

也许这应该是采购订单 AR 的一部分?

实体状态的任何修改都应由实体封装。域服务可以为实体提供功能,而该功能不适合现有实体。

存储库目前正在做他们的工作,即从数据库中持久化数据并用数据“填充”AR。当 AR“保存”它时,存储库会保存任何需要保存的内容。客户 AR 使用采购订单存储库,因此客户可以加载它可能包含的任何采购订单。希望我走在正确的轨道上。

AR、实体或值对象都不应引用存储库或调用其上的任何方法。存储库应由应用程序服务调用。客户端 AR 不太可能包含 PO 集合。相反,客户的采购订单列表应由采购订单存储库提供。关系仍然存在,它只是使用存储库而不是对象遍历来实现。其原因可以追溯到 AR 是一个一致性边界。

控制器当前正在通过客户端工厂工作

工厂应该只用于创建新实例,而不是访问持久化实例——这就是存储库的用途。您可以通过几种方式构建表示层 (MVC)。典型的 DDD 架构是让控制器引用应用程序服务。应用服务反过来通过协调存储库、工厂、基础设施服务和调用 AR 上的行为来实现特定的用例。假设您有一个客户创建新 PO 的用例。代码看起来像:

public ActionResult CreatePurchaseOrder(CreatePurchaseOrderViewModel viewModel)
{
   var poData = viewModel.CreatePurchaseOrderData();

   this.purchaseOrderAppService.CreatePurchaseOrder(viewModel.ClientId, poData);

   return RedirectToAction("Index");
}

...

public class PurchaseOrderAppService
{
  readonly IClientRepository clientDb;
  readonly IPurchaseOrderRepository poDb;

  public void CreatePurchaseOrder(int clientId, PurchaseOrderData poData)
  {
     var client = this.clientDb.Get(clientId);

     var purchaseOrder = PurchaseOrderFactor.Create(client, poData);

     this.poDb.Add(purchaseOrder);
     this.poDt.Commit(); // committing of Unit of Work should be moved up to infrastructure level
  }
}

看看这个DDDSample.Net 项目,它包含一个 DDD 项目的 MVC UI 实现。

于 2013-02-06T04:40:20.850 回答