Domain driven design 容错及;域服务中的状态管理
在我的域中,服务用于协调涉及多个聚合和/或其他服务的更高级别行为。例如,订单管理系统在取消订单时需要执行以下步骤:Domain driven design 容错及;域服务中的状态管理,domain-driven-design,Domain Driven Design,在我的域中,服务用于协调涉及多个聚合和/或其他服务的更高级别行为。例如,订单管理系统在取消订单时需要执行以下步骤: 将订单状态更改为“已取消” 撤销任何未决的信用卡交易 添加包含订单取消原因解释的审核条目 将对订单的更改保留到数据存储 引发OrderCanceledEvent(作为消息) 编码这是非常简单的,除了我还需要实现一些额外的关注点: 如果订单不在状态,我无法执行任何操作 “可取消”状态 如果当前用户没有权限,我无法执行任何操作 取消订单的权限 如果我不能撤销信用卡交易,那么整个 操作
- 如果订单不在状态,我无法执行任何操作 “可取消”状态
- 如果当前用户没有权限,我无法执行任何操作 取消订单的权限
- 如果我不能撤销信用卡交易,那么整个 操作应失败,订单应保持其原始状态 陈述
- 添加审核条目失败不会中止操作
- 如果未能持久保存订单,则应中止该操作和订单 应保持其原始状态
(我应该注意,我没有使用事件源,所以不要让OrderCanceledEvent抛出您。)使用事件处理程序解决此问题的一种方法是使用事件源。工作流程如下:
- 收到
命令后,启动CancelOrder
,将订单置于OrderCancellationSaga
状态取消
- 在支付网关确认退款后,该传奇即告完成,订单处于取消状态并持续。此时,在同一事务中,将引发
事件OrderCancelled
- 如果与支付网关的交互失败或被拒绝,订单可以恢复到先前的状态或进入某种错误状态
class OrderCancellationSaga:Saga
,我开始,
,我处理
{
公共医嘱服务{get;set;}
公共付费网关付费网关{get;set;}
//按订单ID关联saga消息
公共覆盖无效配置HowtoFindsaga()
{
配置映射(x=>x.OrderId,x=>x.OrderId);
配置映射(x=>x.OrderId,x=>x.OrderId);
}
//启动取消流程
公共无效句柄(CancelOrderCommand消息)
{
//检查取消是否授权且有效
// ....
//如果需要,可以在此保存以前的状态
this.Data.OrderId=message.OrderId;
this.Data.State=“取消”;
this.Bus.Send(新命令(…);
}
公共无效句柄(ReturnCompletedEvent消息)
{
this.Data.State=“已取消”;
这个.OrderService.CompleteCancellation(…);
MarkAsComplete();
}
//此处理程序可以托管在不同的端点上。
公共无效句柄(RETURNORDER命令消息)
{
尝试
{
此.PaymentGateway.Return(。。。
}
捕获(例外情况除外)
{
this.Bus.Reply(新的PaymentGatewayInteractionFailedEventmessage(…);
}
}
//此处理程序可用于还原整个操作。
公共无效句柄(PaymentGatewayInteractionFailedEvent消息)
{
//或者恢复到先前的状态。
this.Data.Status=“取消失败”;
//调用任何需要的应用程序服务。
//结束传奇,删除状态
MarkAsComplete();
}
}
根据我对Sagas的了解,它们通常被实现为域服务或命令处理程序,这正是我正在做的。我也在寻找一些更具体的东西(带有代码示例)来说明如何做到这一点。例如,顺序是如何恢复的?(请记住,正如我在更新中指出的,我没有使用事件源。)是的,sagas只是将状态与通过某个键关联的消息关联起来的消息处理程序。sagas与事件源正交。例如,您使用的是什么语言/平台?如果与支付网关的交互失败,则订单可以进入任何状态。此状态可以有效地“恢复”cancel操作很有趣。很遗憾,我们没有使用NServiceBus,因此实现并不完全合适,但我将进一步回顾。不管怎样,saga模式应该适用,NServiceBus只是一个实现。
class OrderOrderCancellationSaga : Saga<OrderCancellationSagaData>
,IAmStartedBy<CancelOrderCommand>,
,IHandle<PaymentGatewayInteractionFailedEvent>
{
public OrderService OrderService { get; set; }
public PaymentGateway PaymentGateway { get; set; }
// correlate saga messages by order ID
public override void ConfigureHowToFindSaga()
{
ConfigureMapping<PaymentGatewayInteractionFailedEvent>(x => x.OrderId, x =>x.OrderId);
ConfigureMapping<RefundCompletedEvent>(x => x.OrderId, x => x.OrderId);
}
// start cancellation process
public void Handle(CancelOrderCommand message)
{
// check if cancellation is authorized and valid
// ....
// can save prior state here, if needed
this.Data.OrderId = message.OrderId;
this.Data.State = "Cancelling";
this.Bus.Send(new RefundOrderCommand(...));
}
public void Handle(RefundCompletedEvent message)
{
this.Data.State = "Cancelled";
this.OrderService.CompleteCancellation(...);
MarkAsComplete();
}
// this handler can be hosted on a different endpoint.
public void Handle(RefundOrderCommand message)
{
try
{
this.PaymentGateway.Refund(...
}
catch(Exception ex)
{
this.Bus.Reply(new PaymentGatewayInteractionFailedEventmessage(...));
}
}
// this handler can be used to revert whole operation.
public void Handle(PaymentGatewayInteractionFailedEvent message)
{
// or revert to prior state.
this.Data.Status = "Cancellation Failed";
// call any application services needed.
// finishes saga, deleting state
MarkAsComplete();
}
}