如何在 REST、CQRS 和 EventSourcing 一起做的同时支持 REST 中的命令?

How to support Command in REST while doing REST, CQRS and EventSourcing together?

考虑以下用于联系人资源的粗粒度 REST API

POST          /api/contacts                             
GET           /api/contacts                             
GET           /api/contacts/:id                         
PUT           /api/contacts/:id                         
DELETE        /api/contacts/:id                         

考虑对联系人资源使用事件源,即验证命令并存储事件。所以必须存储每个事件,包括每个字段级别的更改。

CreateContactCommand -> | Contact("john", "doe", 25) | -> ContactCreatedEvent
FirstNameChangeCommand -> | Contact("jane", "doe", 25) | -> FirstNameChangedEvent
LastNameChangeCommand -> | Contact("jane", "dear", 25) | -> LastNameChangedEvent
AgeChangeCommand -> | Contact("jane", "doe", 30) | -> AgeChangedEvent

现在,结合 REST 和 EventSourcing。

执行 REST,客户端如何与上述标准 REST APIs 通信以进行字段级更改以在服务器端 REST 端点生成命令?

主要问题是,如何设计 REST API 使其也可以支持最终支持事件源的命令?

如果有人能阐明这一点,将不胜感激。

CQRS 和事件溯源既不是 API 设计原则,也不是顶层架构。不过,如果您想 'expose' 您的 API 作为 基于任务的 API,您可以公开 link 作为联系人 资源。

GET /contacts/1234

回应

200 OK
<contact>
  <atom:link href="/contacts/1234/first-name" rel="first-name" />
  <atom:link href="/contacts/1234/last-name" rel="last-name" />
  <atom:link href="/contacts/1234/age" rel="age" />
  <first-name>Jane</first-name>
  <last-name>Doe</last-name>
  <age>25</age>
</contact>

此处假设您将 API 更改为 true level 3 REST API.

此外,/contacts/1234 将只接受 GETDELETE PUT)请求。如果客户想要改变,例如联系人的名字,它必须跟在关系类型为 first-name 的 link 之后,并针对该资源发出 PUT 请求:

PUT /contacts/1234/first-name
<first-name>John</first-name>

此处除 first-name 字段 PUT 之外的任何其他内容都应忽略或拒绝。

因此,当服务收到针对 first-name 资源的 PUT 时,这是一个更改联系人名字的命令。

这仍然不是一个正确的基于任务的API,因为它没有捕获为什么名字变化,但我希望你能理解。

REST 是 Web 的分布式应用架构风格。 Web 有自己的规则和约束,它是表示和资源的领域,而不是任务、命令或查询。

Web 的级别低于您的应用程序。在设计 HTTP 资源时,您必须考虑缓存、版本控制、可重复性、性能、Web 上下文中的松散耦合,因此您可能不想让它们与域实体完全匹配,更不用说这些实体的属性了。此外,HTTP 的动词数量非常有限,当动词让你失望时,资源不一定是描述任务或动作的最佳选择。

因此,在您的域中有意义的命令与(资源,动词)对之间的严格 one-to-one 对应并不总是可取或适当的。您可能想要更进一步,为 REST 客户端设计您自己的域应用协议,以便根据更精细的规则与服务器对话。正如 Mark 指出的那样,DAP 反映在超媒体链接和转换关系中,但您也可以使用自定义 content-types 来更好地描述请求或响应中的有效负载类型。例如,它们可以包含您发送的域命令的类型。

如果您查看 this article,您会发现(POSTapi/inventoryItem/{id})这对夫妇在 "Resources" 下的图表中并不是唯一的。它可用于传输 RemoveItemsFromInventoryCommandCheckInItemsToInventoryCommand。您如何在 HTTP 请求级别指定它是通过使用自定义内容类型 header : Content-Type:application/json;domain-model=RemoveItemsFromInventoryCommandContent-Type:application/json;domain-model=CheckInItemsToInventoryCommand.