由于事件溯源(Event Sourcing)的需要,领域事件需要被保存到外部的存储系统中。由于事件本身描述了在特定对象上所发生的事情,因此,为了能够跟踪对象状态的变化过程以获得Event Audit的能力,我们总是将事件的数据保存在存储系统中,而从来不去删除它们。或许你会认为,这样做有点极端,时间长了,存储系统中的数据量将变得非常庞大。遇到这种情况,你需要引入备份和归档策略,而不是直接将过期的数据删除,因为,存储成本是便宜的,但数据却是有价值的。
对于一些生命周期比较长的领域对象而言,发生在它们身上的事件数量会随着时间的推移而增大,甚至会变得巨大。于是,通过使用这大量的事件数据来重建领域模型将变得非常耗时。因此,我们需要引入“快照”的概念。当领域对象满足一个特定的条件时(快照策略),系统就会为之生成一个“快照”。比如,系统架构师可以设置:当有n个事件发生在某个领域对象上时,将会为这个对象产生“快照”。下次当系统需要重建这个对象时,只需要从“快照”存储中获得最近一次的快照数据,通过快照数据产生对象,进而再逐个地将发生在“快照”之后的事件逐个地“重现”在这个对象上,于是,对象就可以被快速地还原到最后一个事件发生时的状态。
就Apworks框架而言,当前所支持的快照策略非常简单。它由Apworks.Events.Storage.IDomainEventStorage.CanCreateOrUpdateSnapshot方法定义,而Apworks.Events.Storage.SqlDomainEventStorage类则用这样一种快照策略实现了这个接口:每当第1000个事件发生时,系统就会为相应的领域对象做一次快照。因此,如果你打算在你的应用程序中继续采用SQL Server作为事件溯源的存储系统,并且打算定义自己的快照策略的话,你可以创建一个继承于Apworks.Events.Storage.SqlDomainEventStorage的类,并重写CanCreateOrUpdateSnapshot方法。如果你打算选用其它的存储系统(比如MySQL,Oracle,NoSQL方案或者内存数据库等),那么你就需要创建一个实现Apworks.Events.Storage.IDomainEventStorage接口的类,然后实现CanCreateOrUpdateSnapshot方法。
现在让我们返回到TinyLibraryCQRS解决方案,在前面的章节中,我们创建了两个聚合根:Reader和Book,在这两个聚合根里,有两个未实现的方法。现在我们来实现这两个方法以便让我们的应用程序支持快照功能。
-
右键单击TinyLibrary.Domain项目,单击Add |
New Folder菜单,将New Folder重命名为Snapshots
-
用下面的方法创建一个BookSnapshot类
1: using System;
2: using Apworks.Snapshots;
3:
4: namespace TinyLibrary.Domain.Snapshots
5: {
6: [Serializable]
7: public class BookSnapshot : Snapshot
8: {
9: public string Title { get; set; }
10: public string Publisher { get; set; }
11: public DateTime PubDate { get; set; }
12: public string ISBN { get; set; }
13: public int Pages { get; set; }
14: public bool Lent { get; set; }
15: }
16: }
注意我们使用System.SerializableAttribute特性标注了上面的类,因为我们需要将快照数据保存到存储系统中。你也会注意到,这个类的结构跟Book实体的结构很像,这是因为快照本身的作用就是反映并存储与之对应的实体在某个时间点的状态。
-
同理,添加ReaderSnapshot类
1: using System;
2: using System.Collections.Generic;
3: using Apworks.Snapshots;
4:
5: namespace TinyLibrary.Domain.Snapshots
6: {
7: [Serializable]
8: public class ReaderSnapshot : Snapshot
9: {
10: public string LoginName { get; set; }
11: public string Name { get; set; }
12:
13: public List<BookSnapshot> Books { get; set; }
14: }
15: }
-
回到Book类,用下面的方式实现DoBuildFromSnapshot和DoCreateSnapshot方法
1: protected override void DoBuildFromSnapshot(ISnapshot snapshot)
2: {
3: BookSnapshot bs = (BookSnapshot)snapshot;
4: this.Title = bs.Title;
5: this.Publisher = bs.Publisher;
6: this.PubDate = bs.PubDate;
7: this.ISBN = bs.ISBN;
8: this.Pages = bs.Pages;
9: this.Lent = bs.Lent;
10: }
11:
12: protected override ISnapshot DoCreateSnapshot()
13: {
14: BookSnapshot bs = new BookSnapshot();
15: bs.ISBN = this.ISBN;
16: bs.Lent = this.Lent;
17: bs.Pages = this.Pages;
18: bs.PubDate = this.PubDate;
19: bs.Publisher = this.Publisher;
20: bs.Title = this.Title;
21: return bs;
22: }
-
回到Reader类,用下面的方式实现DoBuildFromSnapshot和DoCreateSnapshot方法
1: protected override void DoBuildFromSnapshot(ISnapshot snapshot)
2: {
3: ReaderSnapshot rs = (ReaderSnapshot)snapshot;
4: this.Books.Clear();
5: foreach (var bk in rs.Books)
6: {
7: this.Books.Add(Book.Create(bk.AggregateRootId,
8: bk.Title,
9: bk.Publisher,
10: bk.PubDate,
11: bk.ISBN,
12: bk.Pages,
13: bk.Lent));
14: }
15: this.LoginName = rs.LoginName;
16: this.Name = rs.Name;
17: }
18:
19: protected override ISnapshot DoCreateSnapshot()
20: {
21: ReaderSnapshot rs = new ReaderSnapshot();
22: rs.Books = new List<BookSnapshot>();
23: foreach (var bk in this.Books)
24: {
25: rs.Books.Add((BookSnapshot)bk.CreateSnapshot());
26: }
27: rs.LoginName = this.LoginName;
28: rs.Name = this.Name;
29: return rs;
30: }
-
在完成了上面的所有步骤后,我们的Reader和Book类定义如下:
1: using System;
2: using Apworks;
3: using Apworks.Snapshots;
4:
5: namespace TinyLibrary.Domain
6: {
7: public class Book : SourcedAggregateRoot
8: {
9: public string Title { get; private set; }
10: public string Publisher { get; private set; }
11: public DateTime PubDate { get; private set; }
12: public string ISBN { get; private set; }
13: public int Pages { get; private set; }
在下一篇文章中,我们将引入领域事件,并逐步向对象中加入领域逻辑。
分享到:
相关推荐
在Apworks框架发布Alpha版本的时候,我已经针对其开发案例:TinyLibraryCQRS写了Walkthrough文档,地址是:...Apworks是一套应用程序开发框架,软件架构师和开发人员可以使用这套开发框架开发出面向领域(Domain-Centri
让领域驱动设计变得触手可得 - 基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架。
magic-bottle项目是一套匿名社交系统,包括Andriod、WEB管理端以及服务端,采用DDD+CQRS架构 magic-bottle项目是一套匿名社交系统,包括Andriod、WEB管理端以及服务端,采用DDD+CQRS架构 magic-bottle项目是一套...
一个教科书式的基于C#的CQRS架构,对了解领域驱动设计和命令职责分离原则的思想有很大的帮助。
FoxOffice - 此示例程序展示了如何基于CQRS和事件源构建分布式云.NET Core程序
commanded, 使用命令建立 Elixir cqrs/es应用程序 命令使用命令在 cqrs/es Pattern 之后构建你自己的tcm应用程序。提供支持:命令注册和调度。托管和委托聚合。事件处理。长时间运行的流程管理器。命令为你的构建...
我们正在创造一个参考指南/电子书名为“构建和开发集装箱和微服务基于.NET应用程序”,其详细阐述了如何开发这种建筑风格(微服务,多克尔容器,领域驱动设计某些微服务)以及其他更简单的架构风格,如可以作为...
如何实现CQRS架构
为了运行这个应用程序,你需要在你的开发环境中安装 java8 SDK、docker、docker-compose 和 docker-machine。 如何设置开发环境: - 在多个环境中安装 Java 8 SDK 的指南。 - 在多个环境中安装 docker 的指南。 - 在...
event-sourcing, Meteor的CQRS和事件采购基础架构 用于 Meteor的 CQRS &事件源 这个包提供了构建你的Meteor 应用程序的基础设施,以英镑 ( 命令/查询职责分离) 和英镑事件采购原则为基础。 这样可以使用强大的业务...
具有CQRS架构的Todolist应用程序。 用建设。 演示版 请访问。 管理面板和待办事项历史记录快照 markov链摘要 安装 克隆此模块,然后将服务器指向/ public。 运行composer install来下载依赖项。 要生成数据库,...
本项目是展示如何使用ENode开发基于DDD,CQRS,ES架构的应用程序。 共分为三个Bounded Context: 1.ConferenceManagement,负责会议位置后台管理 2.Registration,负责处理用户下单 3.Payments,负责处理支付 运行步骤...
应用程序 模拟最终用户添加收入或费用交易的银行帐户方案,并在异步事件源和CQRS架构中对其进行处理以重新计算用户的银行帐户余额。 用户还可以请求其帐户余额。 在这里,您可以看到设计: 部署外部服务 docker-...
CQRS(Command Query Responsibility Segration)架构,大家应该不会陌生了。简单的说,就是一个系统,从架构上把它拆分为两部分:命令处理(写请求)+查询处理(读请求)。然后读写两边可以用不同的架构实现,以...
开始之前想先说一下微服务架构和CQRS架构的区别和联系。微服务架构现在很热,到处可以看到各大互联网公司的微服务实践的分享总结。但是,我今天的分享和微服务没有关系,希望可以带给大家一些新的东西。如果一定要说...
用于创建应用程序的基础结构。 安装 使用简单,运行: composer require gpslab/cqrs 命令 处理程序 定位器和订户 ( ) (Symfony 3.3了 ) 序列化命令 简单用法命令 方法中的命令旨在更改应用程序中的数据。 ...
1.0.0版的目标帮助使用编写CQRS和事件源应用程序状态它仍处于非常早期的开发阶段。不要使用版本0.0.5,master分支与此完全不同。建筑要求Java 8 Maven(经过3.5.0+测试) Docker compose(已通过1.18.0测试)适用于...
所以也想谈一下,CQRS架构下是如何实现高性能的。关于CQRS(CommandQueryResponsibilitySegration)架构,大家应该不会陌生了。简单的说,就是一个系统,从架构上把它拆分为两部分:命令处理(写请求)+查询处理(读...