通过REST更新DDD

时间:2016-06-17 21:18:26

标签: c# asp.net-web-api2 domain-driven-design

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

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

supportTicket.Resolve(); 

我很清楚,我可以使用/api/tickets/5/resolve之类的端点来实现这一目标,但如果我想提供一种以原子方式更新整个票据的方法呢?

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

{"status" : "RESOLVED", "Title":"Some crazy title"}

我是否需要在ApplicationSercvice

中执行类似的操作
if(DTO.Status != null && dto.Status == "RESOLVED")
  supportTicket.Resolve();
if(DTO.Title != null)
  supportTicket.setNewTitle(DTO.title);

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

4 个答案:

答案 0 :(得分:7)

Domain Driven Design for RESTful Systems - 吉姆韦伯

  

如果我想提供一种以原子方式更新整张票的方法呢?

如果您想以原子方式更新整个票据,请弃用汇总数据;如果您真正想要的是具有CRUD语义的键值存储,则聚合是您框中的错误工具。

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

  

例如,用户可以向/ api / tickets / 5

发出PUT请求

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

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

PUT /api/tickets/5/messages/{messageId}

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

if(DTO.Status != null && dto.Status == "RESOLVED")
  supportTicket.Resolve();
if(DTO.Title != null)
  supportTicket.setNewTitle(DTO.title);

这没关系,但在实践中更常见的是让消息明确表明要做什么。

{ "messageType" : "ResolveWithNewTitle"
, "status" : "RESOLVED"
, "Title":"Some crazy title"
}

甚至......

[
  { "messageType" : "ChangeTitle"
  , "Title" : "Some crazy title"
  } 
, { "messageType" : "ResolveTicket"
  }
]

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

  

假设我有聚合所需的业务逻辑,但除此之外,对原子更新功能有了新的需求,我正在努力了解处理这个问题的最佳方法。

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

在聚合根目录中实现所需的任何新方法。

在域中正确支持用例之后,您就可以开始担心以前的模式 - 资源只接受传入的请求,并调用相应的命令。

答案 1 :(得分:2)

更改标题是解决故障单的要求吗?如果没有,它们不应该是DDD中的相同动作。如果新名称无效,您不希望不解析票证,如果票证无法解析,您也不希望不更改名称。

进行2次调用以执行2个单独的操作。这也允许灵活性,例如,可以立即更改标题,但是可能“解决”故障单将在实际解决故障单之前启动一些复杂且耗时(异步)的工作流程。也许需要让经理签字?你不希望电话改变那个混合中的“标题”。

如果需要,根据@ VoiceOfUnreason的评论创建一些内容来编排多个命令。

尽可能将事物分开,并使用案例进行编码,而不是最小化与实体的交互。

答案 2 :(得分:0)

你可能是对的。但是,通过制作一个" change()"而将这种逻辑封装在自己的票中可能更明智。方法,接收changeCommandModel(或类似的东西),以便您可以在域对象中定义业务规则。

答案 3 :(得分:0)

  if (DTO.Status != null && dto.Status == "RESOLVED")
                  supportTicket.Resolve(DTO.title);

我将更改底层方法以将title作为参数,这澄清了resolve操作。在domain方法中你需要的第二个if和validation。这是非常偏好,更重要的是消息,我同意@VoiceOfUnreason的第二个选项。