事件溯源的关系型数据库模式

12

我正在尝试将领域事件存储在Postgres数据库中。我对很多事情都不确定,也不想以后重新设计这个结构,因此我正在寻找有经验的事件溯源人员的指导。我目前拥有以下表格:

domain events
    version - or event id, integer sequence, helps to maintain order by replays
    type - event type, probably classname with namespace
    aggregate - aggregate id, probably random string for each aggregate
    timestamp - when the event occured
    promoter - the promoter of the event, probably user id
    details - json encoded data about the properties

我不确定的是:

  1. 我是否应该存储域事件的发起者?
    这可能有助于通过安全漏洞找到被攻击的帐户,但是对于像CRON作业这样的情况,我不知道要存储什么信息。
  2. 我应该用什么格式来存储事件类型?
    我应该添加一个包含事件类型的表格,还是类名就足够了?
    我需要添加事件组吗?
  3. 我对有界上下文的定义感到困惑。据我所知,每个聚合都可以拥有多个有界上下文,因此我可以在多个模块中使用单个聚合的不同方面。这听起来很不错,例如账户可以与许多东西相关联,包括身份验证、授权、用户配置文件、用户帖子、用户合同等等...
    我不确定一个域事件是否可以拥有多个有界上下文,或仅有一个,所以我是否也应该存储事件上下文呢?(对于我想重播与单个上下文相关的事件的情况)
    如何在单个聚合类中实现这么多属性,我应该使用某种组合吗?

第41页http://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf。只是为了信息目的,我对事件溯源并不是很有经验。 - Tudor
2
如果您正在使用.NET,那么可以使用NEventStore库来完成此操作。如果没有,您可以尝试查看由Greg Young构建的GetEventStore - MikeSW
谢谢,我会阅读它们,也许我能找到一些问题的答案。 - inf3rno
1个回答

10

1.我应该存储域事件的发起者吗?

我认为,如果将发起者作为事件负载的一部分而不是元数据进行存储,会更加灵活。安全问题应该在域之外处理。并非每个事件都由用户引发,尽管您可以为它们创建一个虚假的用户(例如 CronJob 的 SysAdmin)。

例如:

ManualPaymentMadeEvent { //store this object as details in your schema
    amount,
    by_user//In this case, developers can determine whether store the promoter case by case
}

2. 我应该用什么格式来存储事件类型?
我应该添加一个包含事件类型的表,还是只使用类名就足够了? 我应该添加事件组吗?

我认为只使用类名就足够了。添加另一张表会使事件读取变得复杂(通过连接表),而且我认为只有在类名更改时(更新事件类型表中的一行)才会增加价值。但我认为使用不会带来太多麻烦。

update domain_events set 
    aggregate_type = 'new class name'
where aggregate_type = 'origin class name'

我不确定我理解事件组,您能否添加更多解释?
有时事件用于集成多个上下文。但是每个事件只在一个上下文中引发。例如,ManualPaymentMadeEvent在订购上下文中引发,在运输上下文中的事件监听器也会消耗它,并将其视为开始运输的触发器。
我喜欢使用每个上下文的每个数据库用户(Oracle术语)。shipping.domain_events适用于运输上下文,ordering.domain_events适用于订购上下文。
这里是axon-framework中的模式,可能会有所帮助。
create table DomainEventEntry (
    aggregateIdentifier varchar2(255) not null,
    sequenceNumber number(19,0) not null,
    type varchar2(255) not null,  --aggregate class name
    eventIdentifier varchar2(255) not null,
    metaData blob,   
    payload blob not null, -- details
    payloadRevision varchar2(255),
    payloadType varchar2(255) not null, --event class name
    timeStamp varchar2(255) not null
);

alter table DomainEventEntry
    add constraint PK_DomainEventEntry primary key (aggregateIdentifier, sequenceNumber, type);

谢谢!我会设计一个简化的、灵感来自于axon的设计。 :-) - inf3rno
哦,顺便问一下。你能推荐一些关于上下文映射的书籍或文章吗?(很难找到理论性的文本,但人们通常会提到它,或者至少是DDD中的有界上下文。) - inf3rno
2
领域驱动设计实施领域驱动设计都有一个单独的章节来介绍这个主题。 - Yugang Zhou
1
@Hippoom,Axon框架在您的示例中是否仅使用一个表(DomainEventEntry)来存储系统中所有聚合类型的所有事件? - Songo
@Songo 默认情况下是这样的。但是自从2.2版本以来,您也可以通过自定义JPA实体为每个聚合根使用一个表格,相关问题请参考此链接 - Yugang Zhou
@Hippoom,是否有可能将该表进行分片,以便每个边界上下文都在单独的分片中拥有其聚合?我的目标是将每个边界上下文作为一个服务,并拥有其独立的数据库。 - Songo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接