通过REST更新DDD

Rob*_*ert 7 c# domain-driven-design asp.net-web-api2

我是DDD的新手,我正试图找出一种通过使用PUT动词来更新聚合的方法.

如果聚合中的所有属性都有私有设置器,那么显然我需要为每个业务需求提供一组功能.举个例子

supportTicket.Resolve(); 
Run Code Online (Sandbox Code Playgroud)

对我来说很清楚,我可以通过诸如此类的端点实现这一点/api/tickets/5/resolve,但是如果我想提供一种以原子方式更新整个票据的方法呢?

例如,用户可以/api/tickets/5使用以下正文发出PUT请求

{"status" : "RESOLVED", "Title":"Some crazy title"}
Run Code Online (Sandbox Code Playgroud)

我是否需要在ApplicationSercvice中执行类似的操作

if(DTO.Status != null && dto.Status == "RESOLVED")
  supportTicket.Resolve();
if(DTO.Title != null)
  supportTicket.setNewTitle(DTO.title);
Run Code Online (Sandbox Code Playgroud)

如果是这种情况并且更改票证标题有一些业务逻辑以防止在票证解决时更改它,我是否应该在更新聚合时考虑某种优先级,或者我认为这完全错误?

Voi*_*son 8

RESTful系统的领域驱动设计 - Jim Webber

如果我想提供一种原子更新整个票的方法怎么办?

如果你想以原子方式更新整个票据,那么就会聚集; 如果您真正想要的是具有CRUD语义的键值存储,则聚合是您框中的错误工具.

只有当域的业务规则强制执行时,聚合才有意义.当你需要的只是铲子时,不要建造拖拉机.

例如,用户可以向/ api/tickets/5发出PUT请求

这会弄得一团糟.在CRUD实现中,通过向其发送新状态的表示来替换资源的当前状态是合适的.但这根本不适合聚合,因为聚合的状态不受您(客户/发布者)的控制.

更合适的习惯是将消息发布到总线上,当由域处理时,会产生实现所需更改的副作用.

PUT /api/tickets/5/messages/{messageId}
Run Code Online (Sandbox Code Playgroud)

现在您的应用程序服务查看消息,并将命令发送到聚合

if(DTO.Status != null && dto.Status == "RESOLVED")
  supportTicket.Resolve();
if(DTO.Title != null)
  supportTicket.setNewTitle(DTO.title);
Run Code Online (Sandbox Code Playgroud)

这是可以的,但实际上更常见的是使消息明确表明要做什么.

{ "messageType" : "ResolveWithNewTitle"
, "status" : "RESOLVED"
, "Title":"Some crazy title"
}
Run Code Online (Sandbox Code Playgroud)

甚至...

[
  { "messageType" : "ChangeTitle"
  , "Title" : "Some crazy title"
  } 
, { "messageType" : "ResolveTicket"
  }
]
Run Code Online (Sandbox Code Playgroud)

基本上,您希望为应用程序提供足够的上下文,以便它可以进行真正的消息验证.

假设我有汇总了哪些封装了所需的业务逻辑,但除此之外,对原子更新功能有了新的需求,我试图了解处理这个问题的最佳方法.

因此,处理此问题的正确方法是首先在域级别处理它 - 与您的域专家坐下来,确保每个人都理解需求以及如何以无处不在的语言表达它等.

在聚合根中实现所需的任何新方法.

一旦你在域中正确支持用例,你就可以开始担心你的资源遵循先前的模式 - 资源只接受传入的请求,并调用适当的命令.