领域驱动设计:避免贫血领域和建模现实世界的角色

g.f*_*ley 6 domain-driven-design domain-model anemic-domain-model

我正在寻找一些关于我应该关注多少避免贫血领域模型的建议.我们刚刚开始使用DDD,并且正在努力解决有关简单设计决策的分析瘫痪问题.我们坚持在最新的点是一定的业务逻辑属于,比如我们有一个Order对象,它具有类似性质Status等.现在说我有像执行命令UndoLastStatus,因为别人犯了一个错误的命令,这不是那么简单因为只需更改Status其他信息就必须记录并更改属性.现在在现实世界中,这是一项纯粹的管理任务.所以我看到它的方式我有两个我能想到的选择:

  • 选项1:将方法添加到订单中,以便类似Order.UndoLastStatus(),虽然这有点意义,它并不真正反映域.也是Order系统中的主要对象,如果涉及订单的所有内容都放在订单类中,事情就会失控.

  • 选项2:创建一个Shop对象,并使用不同的服务代表不同的角色.所以,我可能有Shop.AdminService,Shop.DispatchServiceShop.InventoryService.所以在这种情况下,我会Shop.AdminService.UndoLastStatus(Order).

现在第二个选项我们有更多反映域的东西,并允许开发人员与业务专家讨论实际存在的类似角色.但它也走向了贫血的模式.一般来说哪种方式更好?

Arn*_*psa 6

方案2肯定会导致程序性代码.
可能更容易开发,但更难维护.

现在在现实世界中,这是一项纯粹的管理任务

"管理"任务应该是私有的,并通过公共的,完全"域名"行动来调用.最好 - 仍然用易于理解的代码编写,这些代码是从域驱动的.

正如我所看到的那样 - 问题是UndoLastStatus域专家没什么意义.
他们更有可能在谈论制作,取消和填写订单.

这些方面的东西可能更合适:

class Order{
  void CancelOrder(){
    Status=Status.Canceled;
  }
  void FillOrder(){
    if(Status==Status.Canceled)
      throw Exception();
    Status=Status.Filled;
  }
  static void Make(){
    return new Order();
  }
  void Order(){
    Status=Status.Pending;
  }
}
Run Code Online (Sandbox Code Playgroud)

我个人不喜欢使用"状态",它们会自动共享给使用它们的所有内容 - 我认为这是不必要的耦合.

所以我会有这样的事情:

class Order{
  void CancelOrder(){
    IsCanceled=true;
  }
  void FillOrder(){
    if(IsCanceled) throw Exception();
    IsFilled=true;
  }
  static Order Make(){
    return new Order();
  }
  void Order(){
    IsPending=true;
  }
}
Run Code Online (Sandbox Code Playgroud)

为了在订单状态发生变化时更改相关内容,最好的办法是使用所谓的域事件.
我的代码将沿着这些方向看:

class Order{
  void CancelOrder(){
    IsCanceled=true;
    Raise(new Canceled(this));
  }
  //usage of nested classes for events is my homemade convention
  class Canceled:Event<Order>{
    void Canceled(Order order):base(order){}
  }     
}

class Customer{
  private void BeHappy(){
    Console.WriteLine("hooraay!");
  }
  //nb: nested class can see privates of Customer
  class OnOrderCanceled:IEventHandler<Order.Canceled>{
   void Handle(Order.Canceled e){
    //caveat: this approach needs order->customer association
    var order=e.Source;
    order.Customer.BeHappy();
   }
  }
}
Run Code Online (Sandbox Code Playgroud)

如果秩序变得太大,你可能想要查看有限的上下文是什么(正如埃里克埃文斯所说 - 如果他有机会再次写下他的书,他会将有限的背景移到一开始).

简而言之 - 它是由域驱动的一种分解形式.

想法相对简单 - 从不同的视点(也称为上下文)获得多个订单是可以的.

例如 - 从购物上下文订购,从会计上下文订购.

namespace Shopping{
 class Order{
  //association with shopping cart
  //might be vital for shopping but completely irrelevant for accounting
  ShoppingCart Cart;
 }
}
namespace Accounting{
 class Order{
  //something specific only to accounting
 }
}
Run Code Online (Sandbox Code Playgroud)

但通常足够的域本身可以避免复杂性,如果你足够仔细地听它,它很容易分解.例如,您可能会听到OrderLifeCycle,OrderHistory,OrderDescription这样的专家术语,您可以利用这些术语作为分解的锚点.

注意:请记住 - 我对您的域名一无所知.
我正在使用的那些动词很可能对它完全陌生.