CQRS 和 REST HATEOAS 不匹配
CQRS and REST HATEOAS mismatch
假设您有一个模型 Foo。
一个业务案例是简单地创建一个 Foo 实例,因此在我的模型中有一个相应的 CreateFooCommand,通过调用对给定 REST 端点的 POST 请求触发。
当然还有其他命令。
但是现在,有一个 ViewModel,它是从我的 DomainModel 派生的。它只是一个带有原始数据的 sql table - 来自 DomainModel 的每个 Foo 实例都有对应的派生 ViewModel 实例。两者都有不同的 ID(在 DomainModel 上有一个 DomainID,在 ViewModel 上它只是一个 long
值)。
现在:在这种情况下我还应该关心 HATEOAS 吗?在正确的 REST 实现中,我至少应该 return location-url 在 header 中。但由于我的视图模型仅派生自 DomainModel,我应该关心吗?在创建我的 DomainModel 时,我什至没有视图模型的 ID。
由于 CQRS 意味着查询与命令分离,您可能无法立即执行查询,因为命令可能尚未应用(也许永远不会应用)。
为了与 HATEOAS 协调,服务可以 return 202 Accepted:[=14= 而不是从 POST 请求 returning 200 OK ]
The request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon, as it might be disallowed when processing actually takes place. There is no facility for re-sending a status code from an asynchronous operation such as this.
The 202 response is intentionally non-committal. Its purpose is to allow a server to accept a request for some other process (perhaps a batch-oriented process that is only run once per day) without requiring that the user agent's connection to the server persist until the process is completed. The entity returned with this response SHOULD include an indication of the request's current status and either a pointer to a status monitor or some estimate of when the user can expect the request to be fulfilled.
(我的重点)
该指针可以是 link 客户端可以查询该指针以获取命令的状态。 When/if 命令完成并更新视图,然后该状态资源可以包含视图的 link。
这几乎是直接来自 REST in Practice 的工作流程 - 让人想起它的 Restbucks 示例。
处理 ID 问题的另一种选择是在接受命令之前生成 ID - 甚至可能要求客户端提供 ID。阅读有关此类选项的更多信息 here。
正如 Greg Young 所解释的那样,CQRS 只不过是 "splitting one object into two"。因此,假设您有一个域聚合并且它有一个 id。现在您正在谈论具有另一个 ID 的视图模型。但是,除非您的视图模型中也有聚合 ID,否则您无法更新视图模型。在我看来,您的 REST POST 请求应该 return 一个包含聚合 ID 的结果。这个是你的id,view model id除了读取模型存储之外没有任何人感兴趣。
是否应该 return 像 Mark 建议的命令状态 URI 是另一个讨论的主题。许多 CQRS 从业者目前倾向于同步处理命令,以避免在出现故障时 FE/BE 不匹配,并使 FE 能够对 BE 上的错误做出反应。为一个用户异步执行命令并没有真正的胜利。命令确实会改变状态,在 99% 的情况下,用户需要知道状态是否正确改变。
假设您有一个模型 Foo。 一个业务案例是简单地创建一个 Foo 实例,因此在我的模型中有一个相应的 CreateFooCommand,通过调用对给定 REST 端点的 POST 请求触发。
当然还有其他命令。
但是现在,有一个 ViewModel,它是从我的 DomainModel 派生的。它只是一个带有原始数据的 sql table - 来自 DomainModel 的每个 Foo 实例都有对应的派生 ViewModel 实例。两者都有不同的 ID(在 DomainModel 上有一个 DomainID,在 ViewModel 上它只是一个 long
值)。
现在:在这种情况下我还应该关心 HATEOAS 吗?在正确的 REST 实现中,我至少应该 return location-url 在 header 中。但由于我的视图模型仅派生自 DomainModel,我应该关心吗?在创建我的 DomainModel 时,我什至没有视图模型的 ID。
由于 CQRS 意味着查询与命令分离,您可能无法立即执行查询,因为命令可能尚未应用(也许永远不会应用)。
为了与 HATEOAS 协调,服务可以 return 202 Accepted:[=14= 而不是从 POST 请求 returning 200 OK ]
The request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon, as it might be disallowed when processing actually takes place. There is no facility for re-sending a status code from an asynchronous operation such as this.
The 202 response is intentionally non-committal. Its purpose is to allow a server to accept a request for some other process (perhaps a batch-oriented process that is only run once per day) without requiring that the user agent's connection to the server persist until the process is completed. The entity returned with this response SHOULD include an indication of the request's current status and either a pointer to a status monitor or some estimate of when the user can expect the request to be fulfilled.
(我的重点)
该指针可以是 link 客户端可以查询该指针以获取命令的状态。 When/if 命令完成并更新视图,然后该状态资源可以包含视图的 link。
这几乎是直接来自 REST in Practice 的工作流程 - 让人想起它的 Restbucks 示例。
处理 ID 问题的另一种选择是在接受命令之前生成 ID - 甚至可能要求客户端提供 ID。阅读有关此类选项的更多信息 here。
正如 Greg Young 所解释的那样,CQRS 只不过是 "splitting one object into two"。因此,假设您有一个域聚合并且它有一个 id。现在您正在谈论具有另一个 ID 的视图模型。但是,除非您的视图模型中也有聚合 ID,否则您无法更新视图模型。在我看来,您的 REST POST 请求应该 return 一个包含聚合 ID 的结果。这个是你的id,view model id除了读取模型存储之外没有任何人感兴趣。
是否应该 return 像 Mark 建议的命令状态 URI 是另一个讨论的主题。许多 CQRS 从业者目前倾向于同步处理命令,以避免在出现故障时 FE/BE 不匹配,并使 FE 能够对 BE 上的错误做出反应。为一个用户异步执行命令并没有真正的胜利。命令确实会改变状态,在 99% 的情况下,用户需要知道状态是否正确改变。