希望确保我能够清楚地随意剥离东西。
EVENT STORE数据库
| p_key | invoice_id | Event type | Version | Data |
|-------|------------|-------------------|---------|------|
| 1 | 41234 | Invoice_Generated | 1 | JSON |
| 2 | 34241 | Invoice_Generated | 1 | JSON |
| 3 | 12345 | Invoice_Generated | 1 | JSON |
| 4 | 12345 | Invoice_Reviewed | 2 | JSON |
| 5 | 12345 | Invoice_Paid | 3 | JSON |
JAVA侧面组件
事件存储负责检索事件列表,并在完成所有操作后将事件保存到数据库。
public interface EventStore {
EventStream loadEventStream(AggregateId aggregateId);
void store(AggregateId aggregateId, long version, List<Event> events);
}
事件基本上是从数据库中检索到的行之一。
public interface Event<T> {
AggregateId getAggregateId();
int getVersion();
String getEventType();
void applyOn(T account);
}
我没有得到的是事件流的使用。对我来说,为什么我需要一个事件流
是没有意义的public interface EventStream extends Iterable<Event> {
long version();
void addAll(List<Event> changes);
}
事件流的唯一目的是能够迭代那些听起来不那么有用的事件列表,也许我错过了一些东西,但为什么我不能摆脱事件流并将其称为一天?
答案 0 :(得分:1)
这个问题是关于事件源的非常低级别的视图,并且很大程度上取决于事件存储的实际实现。总而言之,我可以给你一个答案,希望能够了解你对事件商店的理解。
事件流的唯一目的是能够迭代那些听起来不那么有用的事件列表,也许我错过了一些东西,但为什么我不能摆脱事件流并将其称为一天?
是的,事件流提供了迭代可能的大型事件列表的方法,而不是以阻塞的方式从事件存储中检索所有事件。通常,它仅用于读取事件,因此其接口不包含将事件追加到事件存储的方法。
因此,客户端代码只需要来自流的事件。
将事件添加到事件存储时,为了防止并发写入,需要传递事件流的预期版本。可以通过对方法version
使用EventStore.appendEvents(expectedVersion, newEvents)
参数来执行此操作,或者它可以传递先前加载的事件流,并让Event存储检索最后看到的version
,从而减少耦合客户端代码实际执行事件流锁定机制。因此,附加方法的签名可能是这样的:
EventStore.appendEvents(previousEventStream, newEvents)
因此,客户端代码不知道/关心事件存储使用哪种锁定机制(乐观或悲观)来防止并发写入。
可以找到一个例子here (免责声明:它是我的):
public function appendEventsForAggregate(AggregateDescriptor $aggregateDescriptor, $eventsWithMetaData, AggregateEventStream $expectedEventStream): void;
答案 1 :(得分:0)
根据JSON字段中包含的数据的丰富程度,可以执行各种有趣的流处理形式-例如计算每个客户的生命周期价值(当您遍历每个事件时,不断汇总特定客户的总支出),按类别进行支出汇总(与上述情况相同,每个项目类别仅进行汇总)等。将数据作为流进行处理有助于构建数据生命周期以及窗口抽象(滑动/固定宽度)。