问题标签 [domain-events]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
domain-driven-design - 在响应域事件时加载聚合
我正在实现一个具有域驱动设计和事件溯源的应用程序。我将所有域事件存储在 SQL Server 的 DomainEvents 表中。
我有以下聚合:
每个都封装了自己的域逻辑和不变量。我将它们设计为单独的聚合体,因为一个城市可能有数千(也许更多)公司,而且公司也可能有大量员工。如果这些实体属于同一个聚合,我必须将它们一起加载,这在大多数情况下是不必要的。
调用 Enable 或 Disable 将产生域事件(例如CityEnabled
,CompanyDisabled
或EmployeeEnabled
)。这些事件包含启用或禁用实体的主键。
现在我的问题是一个新要求,如果启用/禁用某个城市,则迫使我启用/禁用所有相关公司。如果启用/禁用公司,则员工也需要这样做。
在我的事件处理程序中,例如,如果CityDisabled
发生了我需要为DisableCompanyCommand
属于该城市的每家公司执行该事件处理程序。
但是我怎么知道哪些公司会受到这种变化的影响呢?
我的想法:
查询事件存储是不可能的,因为我不能使用像'where CityId = event.CityId'这样的条件
让父母知道它的孩子 id 并将所有孩子 id 放在父母产生的每个事件中。这也是一个坏主意,因为事件创建者不应该关心以后谁会使用这些事件。因此,只有属于正在发生的事件的信息才应该在事件中。
为每家公司执行
DisableCompanyCommand
。只有匹配的公司CityId
才会改变他们的状态。即使我会异步执行此操作,它也会在每个公司的这些事件上产生巨大的开销。而且对于每家被禁用的公司,应该重复相同的程序来禁用所有用户。创建读取模型,将 ParentIds 映射到 ChildIds,并根据事件中的 parentId 加载 childIds。这听起来像是最合适的解决方案,但问题是,我如何知道在禁用现有公司的同时是否创建了新公司?
我对上述任何解决方案都不满意。基本上,问题是确定发生事件的受影响聚合。
也许你有更好的解决方案?
rabbitmq - 了解 Vaughn Vernon 的最终一致性、BacklogItem 和任务示例
我正在努力理解如何使用 Vaughn Vernon 的 BacklogItems 和 Tasks 的公开示例来实现最终一致性。到目前为止我所理解的语句是(考虑到他将 BacklogItem 和 Task 拆分为单独的聚合根的情况):
BacklogItem 可以包含一个或多个任务。当 BacklogItem 的任务的所有剩余小时数为 0 时,BacklogItem 的状态应更改为“DONE”
我知道这条规则说你不应该在同一个事务中更新两个聚合根,你应该以最终的一致性来完成。
一旦域服务更新了任务的小时数,TaskRemainingHoursUpdated
应该将事件发布到DomainEventPublisher
与执行代码位于同一线程中的事件。在这里,我对以下问题感到茫然:
- 我想应该有一个订阅者(我猜也住在同一个线程中)应该对
TaskRemainingHoursUpdated
事件做出反应。在您的桌面/Web 应用程序中,您在哪一点执行了对总线的订阅?在您的应用程序初始化时?在应用程序代码中?是否有任何理由将域下标放置在特定位置? - 该下标(在同一个线程中)是否应该调用 BacklogItem 存储库并执行更新?(但这将违反在同一事务中不更新两个聚合的规则,因为这会同步发生,对吗?)。
- 如果你想实现最终的一致性来满足前面提到的规则,我是否真的需要像 RabbitMQ 这样的消息代理,即使 BacklogItem 和 Task 都存在于同一个限界上下文中?
- 如果我使用这个消息代理,我应该有一个后台线程还是只使用 RabbitMQ 队列中的事件然后调度事件来更新产品的东西?
如果有人能对此有所了解,我将不胜感激,因为要完整地描绘它是相当复杂的。
domain-driven-design - DDD - 如何以“返回域事件”模式处理事务?
在 DDD 文献中,返回域事件模式被描述为管理域事件的一种方式。从概念上讲,聚合根保留了一个域事件列表,当您对其执行某些操作时会填充该列表。
当聚合根上的操作完成时,数据库事务在应用服务层完成,然后应用服务迭代域事件,调用事件调度器来处理这些消息。
我的问题是关于我们现在应该如何处理交易。Event Dispatcher 是否应该负责为其处理的每个事件管理新事务?或者应用程序服务是否应该在它调用域事件调度程序的域事件迭代中管理事务?当调度程序使用像 RabbitMQ 这样的基础结构机制时,这个问题是无关紧要的,但是当域事件在进程内处理时,它就是。
与我的问题相关的子问题。您对使用 ORM 挂钩(即:NHibernate 的 IPostInsertEventListener、IPostDeleteEventListener、IPostUpdateEventListener)在聚合根上而不是在应用程序服务中手动执行域事件迭代有什么看法?它是否增加了太多的耦合?是否更好,因为它不需要在每个用例中编写相同的代码(域事件在聚合上循环,如果它不在调度程序内部,则可能创建新事务)?
domain-driven-design - 尚未发生的事情的领域事件命名
好的,我很欣赏标题听起来很奇怪。事件应该总是针对已经发生的事情,例如 OrderCreated、ParcelShipped 等。
但是,我想知道是否有人对以下问题有任何想法。
考虑一个对人员及其工作进行建模的 HR 应用程序。为简单起见,一个人可以拥有一堆工作,并且可以在某个日期结束。这个人有一个 EndJob 操作,它需要一个 endDate。
如果 endDate 在未来,领域事件会是什么?
JobEndedEvent
(这不是真的)
JobEndDateAddedEvent
(这是相当技术性的)
其他有界上下文中的消费者将有兴趣知道作业将要结束,但也可能希望在作业结束时也得到通知。我觉得后者应该是消费者的责任,而不是来源的责任。
欢迎任何想法......谢谢。
domain-driven-design - 未来发生的领域事件
有界上下文是否应该负责为未来过时的事物引发事件?
考虑一个人有一组工作的场景。将来可以结束工作。JobEndScheduled
域将在将结束日期添加到作业时引发“ ”事件。JobEnded
域是否也应该在工作实际结束时引发“ ”事件?
我很想得到社区对此的反馈,因为它一定是一个普遍的问题。我在任何 DDD 书籍中都看不到任何关于此的内容。我对答案有明确的看法,但在这一点上宁愿不影响任何答案。
非常感谢...
注意:上周我问了一个类似的问题,但我过于关注命名方面而不是实际事件本身。我希望这个问题更清楚!
c# - 我是否过于复杂了?(CQRS、领域事件和命令失败)
我正在开发一个带有域事件的 CQRS 框架,我遇到了一种代码味道,我想得到一些建议。
我没有使用事件溯源(ES)。ES 事件与领域事件不同。域事件是域感兴趣的并且可能导致发出新命令。ES 事件对持久存储很感兴趣。一个 ES 事件也可能是一个领域事件,但并不是所有的 ES 事件都是领域事件。
我的问题的根源在于声明:
域事件……可能会导致新命令……</p>
具体来说,当这些命令之一失败时会发生什么。
在我读过的 CQRS 文章中,他们说应用层负责在命令发送到命令总线之前对其进行验证。我不同意这一点有两个原因。1)我不信任客户端(安全欺骗)。2) 随着应用程序的成熟和新事件/处理程序/命令的添加,命令 A 的验证可能不知道它还需要验证命令 Foo。
因此,应用层需要知道命令是否成功。如果失败,应用层需要一些信息来解释原因。但是使用触发事件的命令会发出触发事件的新命令……我最终得到的响应类如下所示:
我想要的只是能够判断一个命令是成功还是失败,如果它失败了,为什么。我最终得到了这个递归树结构。我是否过于复杂了?
c# - 序列化和反序列化域事件以在通用实现中持久化并从事件存储中检索
我将 DDD 与 CQRS 和事件溯源一起使用。我需要在我的自定义实现中使用事件存储(特别是这个事件存储IEventStore
)来持久化和检索域事件,但是我在处理序列化/反序列化的方法上遇到了困难。
这是我正在实现的接口:
在我的实现之外,IEventStore
我可以将每个映射器映射IDomainEvent
到一些可序列化/可反序列化的 EventDto 或 json 字符串。这不是问题。但这些是我的限制:
我的域事件是实现的不可变对象
IDomainEvent
(即:没有设置器)我的域事件并不总是很容易以通用方式序列化/反序列化。它们通常具有抽象或接口属性,因此我的域事件和一些可序列化对象(如字符串 json 或事件 DTO)之间的具体映射器在我的
IEventStore
实现之外决定。我的
IEventStore
实现需要是通用的,如果我添加新的域事件类型,我不需要在IEventStore
实现中触及任何东西我的
/li>IEventStore
实现可以接收注入的一些特定实现IMapper<TSource, TDestination>
,以便我可以使用它们在特定类型(不是接口)之间进行序列化/反序列化。
下面是我的尝试:
正如您可以想象的那样,问题主要在于IDomainEventFactory
实施。我需要一个实现以下接口的类:
这个类需要知道IDomainEvent
它需要在运行时将resolvedEvent反序列化到哪个具体。换句话说,如果要检索的事件是 json 表示,MyThingCreatedEvent
也许我可以使用诸如IMapper<ResolvedEvent, MyThingCreatedEvent>
. 但是,如果要检索的事件是 json 表示,MyThingUpdatedEvent
那么我将需要一个服务,例如IMapper<ResolvedEvent, MyThingUpdatedEvent>
.
我想到了一些方法。
选项 1:
我认为我可以让IDomainEventFactory
实现使用 autofac IComponentContext
,以便在运行时我可以设法做一些_componentContext.Resolve(theNeededType)
. 但我不知道如何检索我需要的 IMapper。也许这是可能的,但我对此表示怀疑。
选项 2: 也许我可以拥有一些地图服务,例如 IBetterMapper,例如
这样我的工厂就可以将知道如何将任何东西反序列化为TDestination
. 但是我会遇到同样的问题:我不知道如何在运行时从字符串创建一个类型,例如,做类似的事情_myBetterMapper.Map<WhichTypeHere>
,并且实现该 Map 方法还有一个额外的问题,我想这需要一些注册表并根据类型选择一个或另一个特定的映射器。
我真的坚持这一点。希望我能得到你们的帮助!:)
更新:我已经实现了自己的解决方案并将项目上传到我的个人仓库中:https ://gitlab.com/iberodev/DiDrDe.EventStore.Infra.EventStore 我采用的解决方案是保持事件存储包装器不可知,但在 DI 注册时为那些有点“特殊”的事件提供自定义序列化器/反序列化器。EventStore 允许添加自定义元数据标头,因此我使用一些自定义标头来指定每个数据流上的具体实现类型,以便在检索持久事件时知道在哪里反序列化。
java - 使用Spring Data Common发布域事件时如何处理没有存储库的聚合根
逻辑是:发布一个事件通知关闭一个netty频道。之前,通过DomainEventPublish
,我在域服务、应用程序服务或catch
. 现在,我发现这是错误的,因为只有聚合根才能发布域事件。
我计划将发布领域事件的方式重构为使用 Spring Data Common。我可以设计一个名为 的聚合根ChannelToClose
,并使用一种注册NeedClose
事件的方法,但我无法发布注册的事件。Spring Data Common 仅在执行save()
存储库时发布注册事件,所以我不知道如何在聚合根不需要持久化时发布事件。
这是评论org.springframework.data.domain.DomainEvents
:
DomainEvents 可用于 Spring Data 存储库管理的聚合根的方法,以将该方法返回的事件发布为 Spring 应用程序事件。
domain-events - 使用Spring Data Common时,是否可以在聚合根的构造函数中注册域事件
聚合将由 some 创建application service
,而不是由另一个聚合。
像这样
期望在应用程序服务结束时aggregate
保存并SomeAggregateCreated
发布一个事件。我测试过,并不总是有效,有时构造函数执行后没有立即注册事件。
这是教师课:
这是TeacherAdded
:
这是AbstractDomainEvent
和DomainEvent
AbstractAggregateRoot
复制自,org.springframework.data.domain.AbstractAggregateRoot<A>
并hasCapturedEvent
添加方法进行测试。
如果我运行这个:
它有时会失败,有时会成功。
.net-core - 粒度域事件
最初,我们使用域事件来处理与外部系统的通信。例如,每次用户更新他的电话号码或他的姓名时,我们都会引发一个 PhoneNumberUpdated 和一个 NameUpdated 事件。然后这些被处理程序捕获并发送到其他系统。
只要我们不需要“聚合”事件,它就可以很好地工作。例如,我们有一项新要求,要求我们在用户更新其姓名和/或电话号码时发送一封电子邮件。使用当前的结构,我们的处理程序将收到多次通知(每个引发的事件一次),这将导致发送多封电子邮件。
让我们的事件更通用似乎不是一个好的解决方案。但是,我们如何聚合在一个事务中引发的多个事件呢?
谢谢 Seb