ReSolve Framework:如何从其他聚合的状态中正确地导出聚合的状态?
ReSolve Framework: How to properly derive an aggregate's state from the state of other aggregates?
我正在用 reSolve 做我的第一个项目,在 DDD、ES 和 CQRS 方面的经验有限。所以,也许有一个非常简单的解决方案,但我还没有找到它。
我的问题:在我的项目中,一个聚合的状态(订单的状态)实际上是从其他聚合的状态(属于该订单的每个工作步骤的状态;可以有两个或多个工作步骤同时进行,每个都有自己的状态,例如错误或完成)。
经过一些研究,我的想法是在工作步骤聚合的每个命令处理程序中调用一个附加命令(类似于 "updateOrderStatus"),例如:
pauseWorkStep: (state, { payload: { orderId } }) => {
async ({ resolve }) => await resolve.executeCommand({
aggregateName: 'Order',
aggregateId: orderId,
type: 'updateOrderStatus',
payload: {}
})
return {
type: WORKSTEP_PAUSED,
payload: {}
}
}
此 "updateOrderStatus"-命令将位于订单集合中。相应的投影函数必须在其读取模型中查询工作步骤的当前状态 - 或多或少像这样(这只是为了展示主要思想):
[ORDER_STATUS_UPDATED]: (state, { timestamp }) => ({
...state,
updatedAt: timestamp,
status: () => {
const workStepStatus = async ({ resolve }) => await resolve.executeQuery({
modelName: 'WorkSteps',
resolverName: 'workStepByOrderId',
resolverArgs: { orderId }
})
switch (workStepStatus) {
case "paused":
// define status
break;
case "finished":
// define another status
...
}
}
})
据此,可以确定订单的状态。
问题是,这可能会导致不一致,因为读取模型可能已过时。
如何在 reSolve 框架中解决这个问题?或者,有没有别的办法做到这一点又不违反DDD、CQRS和ES的原则?
谢谢你帮助我。
您的聚合定义似乎过于细化。聚合根不是实体,它可以是具有单个根的实体的集合。
聚合定义事务和一致性边界。
因此,如果您看到该命令应该影响多个聚合作为一个事务,这很好地表明它们实际上是一个聚合。
在您的情况下,我猜您可以将 Order 设为聚合,因此 pauseWorkStep 命令将发送到 Order,而不是 WorkStep。
其中很深的原因是一致性:保证聚合处于一致状态。在您的方法中没有单个事务,如果一个命令成功而另一个命令失败怎么办?如果其他用户在您之间发送另一个命令怎么办?您无法在命令处理程序中控制它。
如果您确实需要编排多个聚合,则需要创建一个 saga/processManager - 这是一个很长的 运行 业务事务。
另请参阅最近对类似主题的讨论:
我正在用 reSolve 做我的第一个项目,在 DDD、ES 和 CQRS 方面的经验有限。所以,也许有一个非常简单的解决方案,但我还没有找到它。
我的问题:在我的项目中,一个聚合的状态(订单的状态)实际上是从其他聚合的状态(属于该订单的每个工作步骤的状态;可以有两个或多个工作步骤同时进行,每个都有自己的状态,例如错误或完成)。
经过一些研究,我的想法是在工作步骤聚合的每个命令处理程序中调用一个附加命令(类似于 "updateOrderStatus"),例如:
pauseWorkStep: (state, { payload: { orderId } }) => {
async ({ resolve }) => await resolve.executeCommand({
aggregateName: 'Order',
aggregateId: orderId,
type: 'updateOrderStatus',
payload: {}
})
return {
type: WORKSTEP_PAUSED,
payload: {}
}
}
此 "updateOrderStatus"-命令将位于订单集合中。相应的投影函数必须在其读取模型中查询工作步骤的当前状态 - 或多或少像这样(这只是为了展示主要思想):
[ORDER_STATUS_UPDATED]: (state, { timestamp }) => ({
...state,
updatedAt: timestamp,
status: () => {
const workStepStatus = async ({ resolve }) => await resolve.executeQuery({
modelName: 'WorkSteps',
resolverName: 'workStepByOrderId',
resolverArgs: { orderId }
})
switch (workStepStatus) {
case "paused":
// define status
break;
case "finished":
// define another status
...
}
}
})
据此,可以确定订单的状态。 问题是,这可能会导致不一致,因为读取模型可能已过时。
如何在 reSolve 框架中解决这个问题?或者,有没有别的办法做到这一点又不违反DDD、CQRS和ES的原则?
谢谢你帮助我。
您的聚合定义似乎过于细化。聚合根不是实体,它可以是具有单个根的实体的集合。
聚合定义事务和一致性边界。 因此,如果您看到该命令应该影响多个聚合作为一个事务,这很好地表明它们实际上是一个聚合。
在您的情况下,我猜您可以将 Order 设为聚合,因此 pauseWorkStep 命令将发送到 Order,而不是 WorkStep。
其中很深的原因是一致性:保证聚合处于一致状态。在您的方法中没有单个事务,如果一个命令成功而另一个命令失败怎么办?如果其他用户在您之间发送另一个命令怎么办?您无法在命令处理程序中控制它。
如果您确实需要编排多个聚合,则需要创建一个 saga/processManager - 这是一个很长的 运行 业务事务。
另请参阅最近对类似主题的讨论: