我有一个使用Jonathan Oliver的CommonDomain和EventStore的小系统.
我如何对我的聚合根进行单元测试以验证是否引发了正确的事件?
考虑遵循聚合根:
public class Subscriber : AggregateBase
{
private Subscriber(Guid id)
{
this.Id = id;
}
private Subscriber(Guid id, string email, DateTimeOffset registeredDate)
: this(id)
{
this.RaiseEvent(new NewSubscriberRegistered(this.Id, email, registeredDate));
}
public string Email{ get; private set; }
public DateTimeOffset RegisteredDate { get; private set; }
public static Subscriber Create(Guid id, string email, DateTimeOffset registeredDate)
{
return new Subscriber(id, email, registeredDate);
}
private void Apply(NewSubscriberRegistered @event)
{
this.Email = @event.Email;
this.RegisteredDate = @event.RegisteredDate;
} …
Run Code Online (Sandbox Code Playgroud) 我是CQRS的忠实粉丝,但有些事情我仍然无法弄清楚:您如何描述CQRS中的登录过程?因为它是一个查询(检查凭据)和一个命令(创建内存/ sql /任何会话状态).在这种情况下我们不能有异步命令,登录过程必须是同步的.
或者CQRS中不存在会话的概念,因为它暗示某种状态?
雷米
我正在玩DDD并弹出这个问题.我如何加载子聚合根?会出现几个性能问题.想象一下以下示例:
public AggregateRoot1
{
#region
properties
#endregion
public AggregateRoot2 AR2{get;set;}
public IEnumerable<AggregateRoot3> AR3List{get;set;}
(...)
}
Run Code Online (Sandbox Code Playgroud)
如果我在获得AggregateRoot1时加载AggregateRoot2和AggregateRoot3列表,那么图表就会非常庞大.这似乎不是一个好方法.
我有两个选择:
顺便说一句,我不考虑使用延迟加载.
我很期待听到你对AR儿童装载的看法.谢谢
我正在努力决定照顾演示逻辑的最佳位置.我已经使用查询的每个方法分离出我的读取查询(CQRS),并为我的View生成DTO.但我的观点只是散布着变量的模板,它们来自DTO.他们没有任何逻辑.
假设我想做一些事情,比如重新格式化日期的外观,将标志转换为实际的描述性单词,或根据从数据库中查询的内容添加显示内容的条件,等等.我想把这个逻辑放在每个查询中,并且不要担心太干(我发现在某些情况下,如果你干得太多,那么你可能会难以改变,因为你必须检查每个依赖或者希望你的单元测试能够坚持下去).我可能会在这里和那里使用一些"帮助器"进行格式化,我发现我一直在做,但我没有看到需要添加一个完整的"表示层".因此,表示逻辑将驻留在每个查询中,并进入返回的DTO,直接放入View中.这将使CQRS的Read端保持超薄,并且每个View对应于Read查询是有意义的.但我也担心这个表示逻辑中的一些非常特定于域.一个新的开发人员需要查看其他查询并重复相同的格式化技术,而不是直接从原始查询中抛出数据.
这是合理的方法,还是在DDD/CQRS中使用了另一种方法?我很难找到CQRS研究的任何指导.注意:我碰巧使用PHP/MySQL,但我想这个问题与语言无关.
我们有微服务,每个服务都生成由事件采购存储库存储的事件.我们使用Cassandra来存储事件数据.
您可能知道,事件的顺序很重要.
当我们从在不同机器上运行的不同服务生成这些事件时,如何管理在这些服务之间不同步的时间(时间戳),从而导致事件顺序不匹配.
假设我有2个骨料Staff
和Shop
我有AA读模型StaffModel
包含在一个非规范化视图店铺信息(shopId,姓名,地址等)。
业务规则是在单个请求中创建 aStaff
和 a Shop
,所以我有一个CreateStaffService
创建 Staff 并触发StaffCreatedEvent
,然后CreateShopService
侦听器StaffCreatedEvent
,创建一个Shop
,然后触发ShopCreatedEvent
在读取模型方面,我有 4 种设计同步器服务的方法:
订阅StaffCreatedEvent
,创建记录StaffModel
。然后订阅ShopCreatedEvent
,更新StaffModel
使用人员 ID上的商店信息。
ShopCreatedEvent
包含人员信息,同步器服务订阅事件并一次性插入完整的读取模型。但是员工信息与Shop
聚合无关,是否可以将其包含在事件中?
建模StaffModel
并ShopModel
单独更新模型以响应相应的聚合事件。
包装CreateStaffService
并CreateShopService
在单个事务中,触发一个StaffAndShopCreatedEvent
我个人更喜欢选项 2 和选项 4,因为选项 1 很难确保StaffCreatedEvent
总是在 之前到达ShopCreatedEvent
。
请分享您对这个主题的想法和经验。谢谢
更新:
为了避免使用序列号的无序事件消耗,假设我使用数据库序列生成一个序列号,每次递增 1,然后假设我的订阅者消耗事件 1,所以最后处理的事件序列为 1。然后生产者按顺序发布事件 2 、事件 3 和事件 4,并且仅在当前事务成功时才发送事件。所以如果事务 2 创建了一个序号 2,但事务失败并回滚,则事件 …
命令查询责任分离/事件采购架构非常适合我正在启动的项目,每年将有大约10亿次与人们健康保险相关的金融交易.主要优势包括审计历史记录,可扩展性,在多个团队中实施异步兼容的UI,从读取数据库中分离这些事务,通过事件队列简化状态到间歇连接的现场办公室的传输,以及应对重要的业务逻辑变化整个系统的生命周期.
然而,在某些方面,CQRS/ES会出现问题,例如为1亿人分配数字ID,用户安全性,最终的一致性是不可接受的.该系统的某些区域本质上是CRUD,并且不受益于CQRS/ES.最后,我们将在不同的团队和公司中拥有大量开发人员,并且拥有不需要CQRS/ES能力的领域将是一件好事.是否有可能采用混合方法,其中某些区域不是事件来源?我们可以只在读写端同步相关表吗?
CQRS架构的聚合实体是否简化了快照缓存失效?更新可能被缓存的聚合实体的任何事件都可以由无效者监听,并且给定的聚合实体比关系实体更粗糙,我们可以区分写事件这个问题是否可以解决?
我期待每年大约有10亿个活动,需要追踪大约4年的历史.我们可以快照和存档旧事件吗?
是否有事件采购程度?例如,一个在线商店系统AddLineItem
事件可能包括每单位的行项目价格,但依赖于读取方来提取和呈现发票上的产品名称.另一个在线商店可能在事件数据中包含该名称.您如何选择在活动中包含哪些内容?在健康保险中,它可能会限制"假设"分析可以运行 - 如果我们没有包括被保险人的年龄,我们就无法模拟需要它的政策?
有没有一种有趣的方式来模拟事件的事件?例如,管理员进入系统,产品的价格将在未来的某个日期发生变化.我想快照将是价格时间表.我们可以改为添加一个过时的ProductPriceChanged事件吗?我们可以在运行"假设"场景时伪造此类事件吗?(这些聚合必须很少更改,以避免版本号和并发检测问题.)
通常声称CQRS/ES使系统更容易适应未来的业务流程变化.我理解命令列出无处不在的语言中的事件使得讨论和重新配置它们更容易,并且事件源消除了RDBMS模型的一些刚性.但是,事件中的任何变化都不会打破事件的重播吗?如果系统可能会发生变化,您最终会遇到许多版本化事件吗?例如,在网上商店通过更改标准来评估客户是否是金卡持有人?你能快照一切吗?您如何约会这些变化?类似地,你必须小心依赖注入,没有注入的依赖项会影响业务逻辑,否则你会破坏重放?
知道为什么它与.NET世界有关,而在该行业的其他领域不那么受欢迎?
巨大的感谢甚至只是阅读.
我一直在考虑分离读取和写入流,甚至让它们使用不同的数据库,通过水平扩展“读取”数据库来提供最佳的快速数据可用性。我要将我的项目写入数据库,将对象更改通知推送到队列中,以便另一个服务读取消息并将更新的数据复制到一个或多个非规范化的“读取”数据库。考虑到这一点,我发现如果“写入”数据库也被非规范化,这将是糟糕的。
示例:假设有一个名为大学用户的用户配置文件已经毕业。假设我将此信息存储在“写入”数据库中,如下所示:
{ userId: 100, name: "John", universityId: 200, universityName: "Cambridge"}
Run Code Online (Sandbox Code Playgroud)
假设用户打开了他的个人资料进行编辑,当他在编辑时,另一个人(剑桥代表)已将名称从“剑桥”更改为“CU”并保存更改,因此它们同时进入“写入”和“读取”数据库。更新大学名称的服务结束了,现在"想"大学到处都OK。在这一刻之后,第一个用户用旧的大学名称保存更新的个人资料,这个名字将出现在他的个人资料出现的每个地方。
这类问题让我觉得“写”数据库必须始终规范化,不能包含重复项。但是我从来没有从关于 CQRS 的文章中找到这个原则。我想错了吗?
CQRS声明:命令不应该查询读取端.
好.我们来看下面的例子:
用户需要与订单行创建订单,每个订单行包含product_id
,price
,quantity
.
它通过订单信息和订单行列表向服务器发送请求.
服务器(命令处理程序)不应该信任客户端,并且需要验证提供的产品(product_ids)是否存在(否则,将会有大量垃圾).
由于不允许命令处理程序查询读取端,因此它应该以某种方式在写入端验证此信息.
我们在写作方面有什么:存储库.就DDD而言,存储库仅与Aggregate Roots一起运行,存储库只能使用GET BY ID和SAVE.
在这种情况下,唯一的选择是逐个加载所有产品聚合(存储库只有GET BY ID方法).
注意:事件源是用作持久性的,因此一次加载多个聚合以避免对存储库的多个请求会产生问题并且效率不高.
这种情况的最佳解决方案是什么?
PS:一个解决方案是重新设计UI(更像是基于任务的UI),例如:用户首先创建订单(使用常规信息),然后逐个添加产品(每个添加单独的http请求),但我仍然需要支持批量操作(以第三方应用程序为例).
I'm new to Mass Transit and I would like to understand if it can helps with my scenario. I'm building a sample application implemented with a CQRS event sourcing architecture and I need a service bus in order to dispatch the events created by the command stack to the query stack denormalizers.
Let's suppose of having a single aggregate in our domain, let's call it Photo, and two different domain events: PhotoUploaded and PhotoArchived.
Given this scenario, we …
cqrs ×10
architecture ×1
commondomain ×1
event-store ×1
masstransit ×1
mongodb ×1
rabbitmq ×1
servicebus ×1
session ×1
sql-server ×1
timestamp ×1
unit-testing ×1
validation ×1