我已经写了一个事件源聚合,现在实现了一个事件源佐贺。。。我注意到这两者是相似的,并创建了一个事件源对象作为基类,它们都是从中派生的。
我在这里看过一个演示http://blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-ii-of-ii/但是觉得可能有一个问题,因为在进程崩溃的情况下,命令可能会丢失,因为命令的发送在写事务之外?
public void Save(ISaga saga)
{
var events = saga.GetUncommittedEvents();
eventStore.Write(new UncommittedEventStream
{
Id = saga.Id,
Type = saga.GetType(),
Events = events,
ExpectedVersion = saga.Version - events.Count
});
foreach (var message in saga.GetUndispatchedMessages())
bus.Send(message); // can be done in different ways
saga.ClearUncommittedEvents();
saga.ClearUndispatchedMessages();
}
相反,我使用的是Greg Young的EventStore,当我保存EventSourcedObject(聚合或saga)时,顺序如下:
我正在实现传奇的两个方面:
问题
>
据我所知,事件处理程序不应该发出命令(如果命令失败会发生什么?)-但是我可以接受上述内容吗,因为Saga是通过此事件代理控制命令创建(对事件的反应)的实际事物,并且命令发送的任何失败都可以在外部处理(在处理Command dEmittedFromSaga
并在命令失败时重新发送的外部EventHandler中)?
或者我忘了包装事件并将本机命令和事件存储在同一个流中(与基类消息混合在一起-佐贺将同时使用命令和事件,聚合只使用事件)?
网上有没有其他参考资料来实现事件来源的萨加斯?我可以理智地检查我的想法吗?
下面是一些背景代码。
Saga发出要运行的命令(包装在CommandEmittedFromSaga事件中)
下面的命令包装在事件中:
public class CommandEmittedFromSaga : Event
{
public readonly Command Command;
public readonly Identity SagaIdentity;
public readonly Type SagaType;
public CommandEmittedFromSaga(Identity sagaIdentity, Type sagaType, Command command)
{
Command = command;
SagaType = sagaType;
SagaIdentity = sagaIdentity;
}
}
Saga在将来的某个时候请求回调(AlarmRequestedBySaga事件)
报警回调请求被包装在事件旁边,并将在请求的时间或之后对事件进行回击和事件处理:
public class AlarmRequestedBySaga : Event
{
public readonly Event Event;
public readonly DateTime FireOn;
public readonly Identity Identity;
public readonly Type SagaType;
public AlarmRequestedBySaga(Identity identity, Type sagaType, Event @event, DateTime fireOn)
{
Identity = identity;
SagaType = sagaType;
Event = @event;
FireOn = fireOn;
}
}
或者,我可以将命令和事件存储在相同的基类型消息流中
public abstract class EventSourcedSaga
{
protected EventSourcedSaga() { }
protected EventSourcedSaga(Identity id, IEnumerable<Message> messages)
{
Identity = id;
if (messages == null) throw new ArgumentNullException(nameof(messages));
var count = 0;
foreach (var message in messages)
{
var ev = message as Event;
var command = message as Command;
if(ev != null) Transition(ev);
else if(command != null) _messages.Add(command);
else throw new Exception($"Unsupported message type {message.GetType()}");
count++;
}
if (count == 0)
throw new ArgumentException("No messages provided");
// All we need to know is the original number of events this
// entity has had applied at time of construction.
_unmutatedVersion = count;
_constructing = false;
}
readonly IEventDispatchStrategy _dispatcher = new EventDispatchByReflectionStrategy("When");
readonly List<Message> _messages = new List<Message>();
readonly int _unmutatedVersion;
private readonly bool _constructing = true;
public readonly Identity Identity;
public IList<Message> GetMessages()
{
return _messages.ToArray();
}
public void Transition(Event e)
{
_messages.Add(e);
_dispatcher.Dispatch(this, e);
}
protected void SendCommand(Command c)
{
// Don't add a command whilst we are in the constructor. Message
// state transition during construction must not generate new
// commands, as those command will already be in the message list.
if (_constructing) return;
_messages.Add(c);
}
public int UnmutatedVersion() => _unmutatedVersion;
}
我相信前两个问题是对流程经理错误理解的结果(又名萨加斯,见底部术语注释)。
看起来你正试图将其建模为一个反向聚合(就像我曾经做的那样)。问题是:聚合的“社会契约”是其输入(命令)可以随时间变化(因为系统必须能够随时间变化),但其输出(事件)不能。一旦编写,事件就是历史问题,系统必须始终能够处理它们。有了这个条件,就可以从不可变的事件流可靠地加载聚合。
如果您尝试作为process manager实现反转输入和输出,则其输出不能是记录问题,因为随着时间的推移,命令可能会被弃用并从系统中删除。当您尝试用删除的命令加载流时,它将崩溃。因此,建模为反向聚合的流程管理器无法从不变的消息流可靠地重新加载。(嗯,我相信你可以想出一个办法……但这明智吗?)
因此,让我们通过查看流程管理器所替代的内容来考虑实施流程管理器。以管理订单履行等流程的员工为例。您为该用户做的第一件事是在UI中设置一个视图,供他们查看。您要做的第二件事是在UI中制作按钮,让用户根据他们在视图上看到的内容执行操作。例如:“此行已付款失败,因此我单击取消订单。此行已付款成功和订单项缺货,因此我单击更改订单。此订单已挂起,并且已过期1天,因此我单击标记订单进行审查。”。。。等等一旦决策过程定义明确并且开始需要用户太多的时间,您的任务就是将此过程自动化。为了实现自动化,其他一切都可以保持不变(视图,甚至一些用户界面,以便您可以检查),但用户已经变成了一段代码。
“走开,否则我会用一个非常小的shell脚本替换你。”
process manager代码现在定期读取视图,如果存在某些数据条件,则可能会发出命令。本质上,流程管理器的最简单版本是一些在计时器上运行的代码(例如,每小时运行一次),并且取决于特定的视图。那是我开始的地方。。。使用您已有的内容(视图/视图更新程序)和最少的添加(定期运行的代码)。即使您稍后决定需要针对某些用例使用不同的功能,“未来的您”将更好地了解需要解决的具体缺点。
这是一个很好的地方来提醒你高尔定律,也许还有雅格尼。
好的材料很难找到,因为这些概念有非常可塑性的实现,而且有各种各样的例子,其中许多是为了一般目的而过度设计的。然而,这里有一些我在回答中使用的参考资料。
DDD-不断发展的业务流程
请注意,术语Saga与流程管理器有不同的含义。一个常见的saga实现基本上是一个路由条,每个步骤及其相应的故障补偿都包含在该条上。这取决于路由条的每个接收者执行路由条上指定的内容并成功地将其传递到下一跳,或者执行故障补偿并向后路由。当处理由不同组管理的多个系统时,这可能有点过于乐观,因此经常使用流程管理器来代替。有关更多信息,请参阅此SO问题。
SAGA是一个免费的地理信息系统(GIS),与一个特殊的应用程序编程接口(API的)地理数据处理。这个API可以很容易实施的新算法。它的API支持网格数据,矢量数据,并表。 功能和特点 ◆ SAGA是系统自动地学分析的缩写 ◆ 是一个地理信息系统(GIS)软件 ◆ 已设计为一个容易和有效地执行空间算法 ◆ 提供了一个全面的,越来越多的地球科学方法 ◆ 提供了一个容易平易近人的用户界面与许多可视化的
我在我的应用程序中实现了事件源和CQRS模式。我从下载示例代码的CQRS旅程中获得灵感。在那里,我找到了事件源的整个基础设施(命令处理程序、事件处理程序、事件、信封…等等),但它是相当大的代码量,我无法想象我需要所有的代码来进行简单的事件源。 你知道一些常见的测试库/nuget包/项目包含所有的基础设施发送/注册命令,事件和我在事件源模式中需要的一切吗?还是要我自己去实现?
因此,目前我正在用axon框架实现一个saga,使用事件源和CQRS。 情况如下: 我有3个微服务,m1、m2和m3 用户在GUI中输入由m1、m2、m3分别处理和保存的3个实体e1、e2、e3的数据,因此M1->e1、M2->e2、M3->e3 现在来看看这个传奇的必要性: e1没有e2就不能存在,e2没有E3就不能存在。 因此,所有3个实体必须由它们各自的服务成功创建,如果其中一个失败,则sa
我在CQRS/ES设计中有一个计时案例。为了便于讨论,让我们以Microsoft关于这个主题的示例会议管理为基础(https://msdn.microsoft.com/en-us/library/jj554200.aspx)。 假设在第1分钟创建会议(最大座位数为20)。 在第4分钟,事件到达order mgmt上下文,因此创建了一个座位可用性。 在第7分钟,用户下了一个订单(通过订单管理),购买
我想使用Axon框架实现CQRS和ES 我有一个相当复杂的HTML表单,它代表招聘过程的六个步骤。 ES将有助于生成选定日期的历史统计信息并跟踪表单的变化。 null null 此解决方案的缺点是,由于违反约束,可能会生成一些事件,而不会生成其他事件,例如:将成功,但将由于分配未经授权的用户而失败。也许我应该在命令生成之前检查所有约束? 此解决方案的缺点是某些命令可能失败,例如:可能由于分配未经授
具有事件源的CQR看起来非常适合作为我们的一个系统的架构,目前我们只担心一件小事:处理大量事件,并因此处理大型事件商店。 我们当前的系统每天接收大约一百万个事件(目前与事件源无关),如果我们将它们存储在更长的时间内,我们的事件存储可能会变得相当大,但是如果我们经常转储/清除滚动快照,我们可能会失去事件源的一大优势:关于系统历史和重播的信息。 在CQRS架构中处理这个问题的常见方法是什么?这到底是个