使用Apworks开发基于CQRS架构的应用程序(四):领域事件
2011-01-26 16:10
351 查看
根据wikipedia中关于“事件”的描述,“事件”可以被看成是“状态的一次变化”。例如:当一个客户购买了一台汽车,汽车的状态就从“待售”转变为“已售”。汽车销售系统则把这种状态的改变看成是一次事件的产生、发布、检测以及被更多其它应用程序所使用的过程。
对于CQRS架构的应用程序而言,事件产生于领域模型,并由领域模型发布事件同时由领域模型首次捕获并处理,因此,我们称之为领域事件(DomainEvents)。在Apworks开发框架中,与领域事件相关的代码都被定义在Apworks.Events命名空间下。
请使用下面的步骤为TinyLibraryCQRS解决方案添加领域事件。
在SolutionExplorer的TinyLibraryCQRS解决方案上,右键单击并选择Add|NewProject…菜单,这将打开AddNewProject对话框
在InstalledTemplates选项卡下,选择VisualC#|Windows,然后选择ClassLibrary,确保所选的.NETFramework版本是.NETFramework4,然后在Name文本框中,输入TinyLibrary.Events,并单击OK按钮
在SolutionExplorer下,右键单击TinyLibrary.Events项目的References节点,然后选择AddReference…菜单,这将打开AddReference对话框
在.NET选项卡下,选择Apworks,然后单击OK按钮
向TinyLibrary.Events项目添加下面的类代码,以实现领域事件
[/code]
从上面的代码我们可以看到,所有的领域事件都继承于Apworks.Events.DomainEvent类。同时,在每个领域事件上都是用了System.SerializableAttribute特性,以便系统能够序列化/反序列化这些领域事件,进而使其能够在网络上传输,或能够将其保存到存储系统中。
每当一次操作发生在我们的领域对象上(比如Book和Reader)时,这种操作有可能会改变对象的状态。例如:一个用来改变用户姓名的实例方法ChangeName会产生“用户姓名变更”的效果,于是,领域事件就由此产生了,最先被通知到该事件的就是领域对象本身,它会将这个事件记录下来,然后根据事件中所包含的数据来改变相应的状态。下面让我们看看,如何在Apworks框架的支持下,实现事件的产生和捕获。
现在让我们为之前已经定义好的Book和Reader实体添加一些业务逻辑。根据上面的分析我们不难得出,应用程序的用户可以创建读者和图书,不仅如此,读者可以从图书馆借书,也可以向图书馆还书。而对于书而言呢?它会被从图书馆借出,也会被归还回图书馆。于是,我们可以向Reader实体添加BorrowBook和ReturnBook方法,向Book实体添加LendTo和ReturnBy方法。
回到TinyLibrary.Domain项目,右键单击References节点,选择AddReference…菜单,这将打开AddReference对话框
在Projects选项卡下,选择TinyLibrary.Events,然后单击OK按钮
向Book类添加下面的代码
[/code]
向Reader类添加下面的代码
[/code]
在上面定义的业务方法里,用到了RaiseEvent泛型方法。这个方法会通知Apworks框架已经产生了一个新的事件。领域对象有其自己的方法来处理这些新产生的事件,这些处理方法都被冠以Apworks.Events.HandlesAttribute特性。在这些事件处理方法中,领域对象的状态得到了改变。
目前看来,在这些领域对象中到处充斥着被标以Apworks.Events.HandlesAttribute特性的事件处理方法,当前的Apworks框架仅支持这样的基于反射技术的“事件-处理器”映射。在Apworks的后续版本中,会提供更多的映射策略以供开发人员选择。
总之,领域对象通过方法来表述它们需要处理的业务逻辑,这些方法又通过领域事件来更新领域对象的状态。这就使得CQRS架构能够通过领域事件来跟踪对象状态发生变化的情况。每当RaiseEvent泛型方法被调用时,SourcedAggregateRoot基类会将事件记录到本地的存储中,然后调用相应的标有Apworks.Events.HandlesAttribute特性的事件处理函数。当领域仓储保存聚合时,这些保存在本地的事件将被发布到事件总线,以便于订阅者能够对事件做后续处理。
在Apworks框架中,领域事件是由领域仓储推送到事件总线的【注意:这只是在Apworks的Alpha版本中会这么做,由于这么做会导致TPC(或者称2PC,二次提交)问题,所以在后续版本中会解决这个问题】。而在另一方面,系统会根据事件发布策略,将事件总线中的事件发布到与之相应的事件处理器上。这个过程可以通过异步实现以提高系统的响应度。当事件处理器获得了事件之后,它们会与基础结构层服务打交道以实现更多的功能(比如邮件推送、数据同步等等)。在Apworks系统启动的时候,应用程序就会根据配置文件对事件处理器进行注册。在后续章节中,我将简单介绍ApworksAlpha版本中的配置文件。
就TinyLibraryCQRS而言,我们尽创建一些用于同步“查询数据库”的事件处理器。查询数据库是一个位于基础结构层的存储系统,它用来保存与“查询”相关的数据,比如用于生成报表的数据,或者是用于显示在屏幕上的数据等。在后面的讨论中,你将了解到,TinyLibraryCQRS的查询数据库完全是为了迎合客户端的显示需要而设计的:每张数据表对应的是一个表示层的视图模型(ViewModel)。你会觉得这种设计将造成大量的数据冗余,没错,的确数据冗余很大,但这样做减少了JOIN操作甚至是ORM的引入,它有助于性能的提高【注意:在实际项目中,还是应该根据具体情况来确定查询数据库的设计方案】。
在创建事件处理器之前,我们先讨论一下“查询对象”的概念。“查询对象”可以看成是一种DTO(DataTransferObject),它是查询数据库中数据的一种表现形式,会来回在网络中传输并持久化到存储系统。在Apworks框架的应用中,“查询对象”需要实现Apworks.Queries.IQueryObject接口,并使用System.SerializableAttribute特性来标识这些对象。不仅如此,如果你使用Apworks.Queries.Storage.SqlQueryObjectStorage来访问你的查询数据库(实际上,如果你是在使用关系型数据库系统),那么你就需要事先准备一个XML映射文件。这个XML映射文件告知系统,查询对象将被映射到哪张数据表,以及查询对象的属性将被映射到数据表中的哪些字段。这个XML文件的Schema结构非常简单,在后续章节中会对其进行讨论。
现在,让我们创建几个查询对象。
在SolutionExplorer下,右键单击TinyLibraryCQRS解决方案,然后单击Add|NewProject…菜单,这将打开AddNewProject对话框
在InstalledTemplates选项卡下,选择VisualC#|Windows,然后选择ClassLibrary,确保所选的.NET版本是.NETFramework4,然后在Name文本框中,输入TinyLibrary.QueryObjects,然后单击OK按钮
右键单击TinyLibrary.QueryObjects项目的References节点,单击AddReference…菜单,这将打开AddReference对话框
在.NET选项卡下,选择Apworks然后单击OK按钮
向TinyLibrary.QueryObjects项目添加如下代码
[/code]
现在可以开始创建事件处理器了。事件处理器会使用捕获的事件来创建查询对象,然后将其保存到查询数据库中。
在SolutionExplorer中,右键单击TinyLibraryCQRS解决方案,然后单击Add|NewProject…菜单,这将打开AddNewProject对话框
在InstalledTemplates选项卡下,选择VisualC#|Windows,然后选择ClassLibrary,确保所选的.NET版本是.NETFramework4,在Name文本框中,输入TinyLibrary.EventHandlers,然后单击OK按钮
右键单击TinyLibrary.EventHandlers项目的References节点,然后单击AddReference…菜单,这将打开AddReference对话框
在.NET选项卡下,选择Apworks然后单击OK按钮
右键单击TinyLibrary.EventHandlers项目的References节点,然后单击AddReference…菜单,这将打开AddReference对话框
在Projects选项卡下,选择TinyLibrary.Events和TinyLibrary.QueryObjects,然后单击OK按钮
向TinyLibrary.EventHandlers项目加入如下代码:
[/code]
事件处理器需要继承Apworks.Events.EventHandler类,并实现Handle方法。在Handle方法中,事件处理器将首先通过GetQueryObjectStorage方法获得查询数据库存储对象,然后通过存储对象来保存查询对象。
至此,我们已经创建了聚合、快照、查询对象、领域事件以及事件处理器。在下一步操作中,我们将会创建一些“业务外观对象”来协调业务处理过程,以便更上层的应用组件能够方便地使用我们的领域模型。在CQRS架构中,这种“业务外观对象”就是命令与命令处理器。
对于CQRS架构的应用程序而言,事件产生于领域模型,并由领域模型发布事件同时由领域模型首次捕获并处理,因此,我们称之为领域事件(DomainEvents)。在Apworks开发框架中,与领域事件相关的代码都被定义在Apworks.Events命名空间下。
请使用下面的步骤为TinyLibraryCQRS解决方案添加领域事件。
在SolutionExplorer的TinyLibraryCQRS解决方案上,右键单击并选择Add|NewProject…菜单,这将打开AddNewProject对话框
在InstalledTemplates选项卡下,选择VisualC#|Windows,然后选择ClassLibrary,确保所选的.NETFramework版本是.NETFramework4,然后在Name文本框中,输入TinyLibrary.Events,并单击OK按钮
在SolutionExplorer下,右键单击TinyLibrary.Events项目的References节点,然后选择AddReference…菜单,这将打开AddReference对话框
在.NET选项卡下,选择Apworks,然后单击OK按钮
向TinyLibrary.Events项目添加下面的类代码,以实现领域事件
[code]1:usingSystem;
2:usingApworks.Events;
3:
4:namespaceTinyLibrary.Events
5:{
6:[Serializable]
7:publicclassBookBorrowedEvent:DomainEvent
8:{
9:publiclongBookId{get;set;}
10:publiclongReaderId{get;set;}
11:publicstringReaderName{get;set;}
12:publicstringReaderLoginName{get;set;}
13:publicstringBookTitle{get;set;}
14:publicstringBookPublisher{get;set;}
15:publicDateTimeBookPubDate{get;set;}
16:publicstringBookISBN{get;set;}
17:publicintBookPages{get;set;}
18:publicboolBookLent{get;set;}
19:
20:publicDateTimeRegistrationDate{get;set;}
21:publicDateTimeDueDate{get;set;}
22:}
23:
24:[Serializable]
25:publicclassBookCreatedEvent:DomainEvent
26:{
27:publicstringTitle{get;set;}
28:publicstringPublisher{get;set;}
29:publicDateTimePubDate{get;set;}
30:publicstringISBN{get;set;}
31:publicintPages{get;set;}
32:publicboolLent{get;set;}
33:}
34:
35:[Serializable]
36:publicclassBookGetReturnedEvent:DomainEvent
37:{
38:publiclongReaderId{get;set;}
39:publicDateTimeReturnedDate{get;set;}
40:}
41:
42:[Serializable]
43:publicclassBookLentEvent:DomainEvent
44:{
45:publiclongReaderId{get;set;}
46:publicDateTimeLentDate{get;set;}
47:}
48:
49:[Serializable]
50:publicclassBookReturnedEvent:DomainEvent
51:{
52:publiclongReaderId{get;set;}
53:publiclongBookId{get;set;}
54:}
55:
56:[Serializable]
57:publicclassReaderCreatedEvent:DomainEvent
58:{
59:publicstringLoginName{get;set;}
60:publicstringName{get;set;}
61:}
62:}
[/code]
从上面的代码我们可以看到,所有的领域事件都继承于Apworks.Events.DomainEvent类。同时,在每个领域事件上都是用了System.SerializableAttribute特性,以便系统能够序列化/反序列化这些领域事件,进而使其能够在网络上传输,或能够将其保存到存储系统中。
每当一次操作发生在我们的领域对象上(比如Book和Reader)时,这种操作有可能会改变对象的状态。例如:一个用来改变用户姓名的实例方法ChangeName会产生“用户姓名变更”的效果,于是,领域事件就由此产生了,最先被通知到该事件的就是领域对象本身,它会将这个事件记录下来,然后根据事件中所包含的数据来改变相应的状态。下面让我们看看,如何在Apworks框架的支持下,实现事件的产生和捕获。
现在让我们为之前已经定义好的Book和Reader实体添加一些业务逻辑。根据上面的分析我们不难得出,应用程序的用户可以创建读者和图书,不仅如此,读者可以从图书馆借书,也可以向图书馆还书。而对于书而言呢?它会被从图书馆借出,也会被归还回图书馆。于是,我们可以向Reader实体添加BorrowBook和ReturnBook方法,向Book实体添加LendTo和ReturnBy方法。
回到TinyLibrary.Domain项目,右键单击References节点,选择AddReference…菜单,这将打开AddReference对话框
在Projects选项卡下,选择TinyLibrary.Events,然后单击OK按钮
向Book类添加下面的代码
[code]1:publicvoidLendTo(Readerreader)
2:{
3:this.RaiseEvent<BookLentEvent>(newBookLentEvent
4:{
5:ReaderId=reader.Id,
6:LentDate=DateTime.Now
7:});
8:}
9:
10:publicvoidReturnBy(Readerreader)
11:{
12:this.RaiseEvent<BookGetReturnedEvent>(newBookGetReturnedEvent
13:{
14:ReaderId=reader.Id,
15:ReturnedDate=DateTime.Now
16:});
17:}
18:
19:[Handles(typeof(BookLentEvent))]
20:privatevoidOnBookLent(BookLentEventevnt)
21:{
22:this.Lent=true;
23:}
24:
25:[Handles(typeof(BookGetReturnedEvent))]
26:privatevoidOnBookReturnedBack(BookGetReturnedEventevnt)
27:{
28:this.Lent=false;
29:}
[/code]
向Reader类添加下面的代码
[code]1:publicvoidBorrowBook(Bookbook)
2:{
3:if(book.Lent)
4:thrownewDomainException();
5:book.LendTo(this);
6:this.RaiseEvent<BookBorrowedEvent>(newBookBorrowedEvent
7:{
8:BookId=book.Id,
9:ReaderId=Id,
10:ReaderLoginName=LoginName,
11:ReaderName=Name,
12:BookISBN=book.ISBN,
13:BookPages=book.Pages,
14:BookPubDate=book.PubDate,
15:BookPublisher=book.Publisher,
16:BookTitle=book.Title,
17:BookLent=book.Lent,
18:RegistrationDate=DateTime.Now,
19:DueDate=DateTime.Now.AddMonths(2)
20:});
21:}
22:
23:publicvoidReturnBook(Bookbook)
24:{
25:if(!book.Lent)
26:thrownewDomainException();
27:if(!HasBorrowed(book))
28:thrownewDomainException();
29:book.ReturnBy(this);
30:this.RaiseEvent<BookReturnedEvent>(newBookReturnedEvent
31:{
32:ReaderId=this.Id,
33:BookId=book.Id
34:});
35:}
36:
37:privateboolHasBorrowed(Bookbook)
38:{
39:returnBooks.Any(p=>p.Id.Equals(book.Id));
40:}
41:
42:[Handles(typeof(BookBorrowedEvent))]
43:privatevoidOnBookBorrowed(BookBorrowedEventevnt)
44:{
45:this.Books.Add(Book.Create(evnt.BookId,
46:evnt.BookTitle,
47:evnt.BookPublisher,
48:evnt.BookPubDate,
49:evnt.BookISBN,
50:evnt.BookPages,
51:evnt.BookLent));
52:
53:}
54:
55:[Handles(typeof(BookReturnedEvent))]
56:privatevoidOnBookReturned(BookReturnedEventevnt)
57:{
58:this.Books.RemoveAll(p=>p.Id.Equals(evnt.BookId));
59:}
[/code]
在上面定义的业务方法里,用到了RaiseEvent泛型方法。这个方法会通知Apworks框架已经产生了一个新的事件。领域对象有其自己的方法来处理这些新产生的事件,这些处理方法都被冠以Apworks.Events.HandlesAttribute特性。在这些事件处理方法中,领域对象的状态得到了改变。
目前看来,在这些领域对象中到处充斥着被标以Apworks.Events.HandlesAttribute特性的事件处理方法,当前的Apworks框架仅支持这样的基于反射技术的“事件-处理器”映射。在Apworks的后续版本中,会提供更多的映射策略以供开发人员选择。
总之,领域对象通过方法来表述它们需要处理的业务逻辑,这些方法又通过领域事件来更新领域对象的状态。这就使得CQRS架构能够通过领域事件来跟踪对象状态发生变化的情况。每当RaiseEvent泛型方法被调用时,SourcedAggregateRoot基类会将事件记录到本地的存储中,然后调用相应的标有Apworks.Events.HandlesAttribute特性的事件处理函数。当领域仓储保存聚合时,这些保存在本地的事件将被发布到事件总线,以便于订阅者能够对事件做后续处理。
在Apworks框架中,领域事件是由领域仓储推送到事件总线的【注意:这只是在Apworks的Alpha版本中会这么做,由于这么做会导致TPC(或者称2PC,二次提交)问题,所以在后续版本中会解决这个问题】。而在另一方面,系统会根据事件发布策略,将事件总线中的事件发布到与之相应的事件处理器上。这个过程可以通过异步实现以提高系统的响应度。当事件处理器获得了事件之后,它们会与基础结构层服务打交道以实现更多的功能(比如邮件推送、数据同步等等)。在Apworks系统启动的时候,应用程序就会根据配置文件对事件处理器进行注册。在后续章节中,我将简单介绍ApworksAlpha版本中的配置文件。
就TinyLibraryCQRS而言,我们尽创建一些用于同步“查询数据库”的事件处理器。查询数据库是一个位于基础结构层的存储系统,它用来保存与“查询”相关的数据,比如用于生成报表的数据,或者是用于显示在屏幕上的数据等。在后面的讨论中,你将了解到,TinyLibraryCQRS的查询数据库完全是为了迎合客户端的显示需要而设计的:每张数据表对应的是一个表示层的视图模型(ViewModel)。你会觉得这种设计将造成大量的数据冗余,没错,的确数据冗余很大,但这样做减少了JOIN操作甚至是ORM的引入,它有助于性能的提高【注意:在实际项目中,还是应该根据具体情况来确定查询数据库的设计方案】。
在创建事件处理器之前,我们先讨论一下“查询对象”的概念。“查询对象”可以看成是一种DTO(DataTransferObject),它是查询数据库中数据的一种表现形式,会来回在网络中传输并持久化到存储系统。在Apworks框架的应用中,“查询对象”需要实现Apworks.Queries.IQueryObject接口,并使用System.SerializableAttribute特性来标识这些对象。不仅如此,如果你使用Apworks.Queries.Storage.SqlQueryObjectStorage来访问你的查询数据库(实际上,如果你是在使用关系型数据库系统),那么你就需要事先准备一个XML映射文件。这个XML映射文件告知系统,查询对象将被映射到哪张数据表,以及查询对象的属性将被映射到数据表中的哪些字段。这个XML文件的Schema结构非常简单,在后续章节中会对其进行讨论。
现在,让我们创建几个查询对象。
在SolutionExplorer下,右键单击TinyLibraryCQRS解决方案,然后单击Add|NewProject…菜单,这将打开AddNewProject对话框
在InstalledTemplates选项卡下,选择VisualC#|Windows,然后选择ClassLibrary,确保所选的.NET版本是.NETFramework4,然后在Name文本框中,输入TinyLibrary.QueryObjects,然后单击OK按钮
右键单击TinyLibrary.QueryObjects项目的References节点,单击AddReference…菜单,这将打开AddReference对话框
在.NET选项卡下,选择Apworks然后单击OK按钮
向TinyLibrary.QueryObjects项目添加如下代码
[code]usingSystem;
usingSystem.Runtime.Serialization;
usingApworks.Queries;
namespaceTinyLibrary.QueryObjects
{
[Serializable]
[DataContract]
publicclassBookObject:IQueryObject
{
[DataMember]
publiclongId{get;set;}
[DataMember]
publicstringTitle{get;set;}
[DataMember]
publicstringPublisher{get;set;}
[DataMember]
publicDateTimePubDate{get;set;}
[DataMember]
publicstringISBN{get;set;}
[DataMember]
publicintPages{get;set;}
[DataMember]
publicboolLent{get;set;}
[DataMember]
[DataMember]
publicstringLendTo{get;set;}
}
[Serializable]
[DataContract]
publicclassReaderObject:IQueryObject
{
[DataMember]
publiclongId{get;set;}
[DataMember]
publicstringLoginName{get;set;}
[DataMember]
publicstringName{get;set;}
[DataMember]
publiclongAggregateRootId{get;set;}
}
[Serializable]
[DataContract]
publicclassRegistrationObject:IQueryObject
{
[DataMember]
publiclongId{get;set;}
[DataMember]
publiclongBookAggregateRootId{get;set;}
[DataMember]
publiclongReaderAggregateRootId{get;set;}
[DataMember]
publicstringReaderName{get;set;}
[DataMember]
publicstringReaderLoginName{get;set;}
[DataMember]
publicstringBookTitle{get;set;}
[DataMember]
publicstringBookPublisher{get;set;}
[DataMember]
publicDateTimeBookPubDate{get;set;}
[DataMember]
publicstringBookISBN{get;set;}
[DataMember]
publicintBookPages{get;set;}
[DataMember]
publicDateTimeRegistrationDate{get;set;}
[DataMember]
publicDateTimeDueDate{get;set;}
[DataMember]
publicDateTimeReturnedDate{get;set;}
[DataMember]
publicboolReturned{get;set;}
}
}
[/code]
现在可以开始创建事件处理器了。事件处理器会使用捕获的事件来创建查询对象,然后将其保存到查询数据库中。
在SolutionExplorer中,右键单击TinyLibraryCQRS解决方案,然后单击Add|NewProject…菜单,这将打开AddNewProject对话框
在InstalledTemplates选项卡下,选择VisualC#|Windows,然后选择ClassLibrary,确保所选的.NET版本是.NETFramework4,在Name文本框中,输入TinyLibrary.EventHandlers,然后单击OK按钮
右键单击TinyLibrary.EventHandlers项目的References节点,然后单击AddReference…菜单,这将打开AddReference对话框
在.NET选项卡下,选择Apworks然后单击OK按钮
右键单击TinyLibrary.EventHandlers项目的References节点,然后单击AddReference…菜单,这将打开AddReference对话框
在Projects选项卡下,选择TinyLibrary.Events和TinyLibrary.QueryObjects,然后单击OK按钮
向TinyLibrary.EventHandlers项目加入如下代码:
[code]usingSystem;
usingSystem.Data.SqlTypes;
usingApworks.Queries.Storage;
usingApworks.Storage;
usingTinyLibrary.Events;
usingTinyLibrary.QueryObjects;
namespaceTinyLibrary.EventHandlers
{
publicclassBookBorrowedEventHandler:Apworks.Events.EventHandler<BookBorrowedEvent>
{
publicoverrideboolHandle(BookBorrowedEventtarget)
{
using(IQueryObjectStoragestorage=this.GetQueryObjectStorage())
{
RegistrationObjectregistrationObject=newRegistrationObject
{
BookAggregateRootId=target.BookId,
BookISBN=target.BookISBN,
BookPages=target.BookPages,
BookPubDate=target.BookPubDate,
BookPublisher=target.BookPublisher,
BookTitle=target.BookTitle,
ReaderAggregateRootId=target.ReaderId,
ReaderLoginName=target.ReaderLoginName,
ReaderName=target.ReaderName,
DueDate=target.DueDate,
RegistrationDate=target.RegistrationDate,
Returned=false,
ReturnedDate=(DateTime)SqlDateTime.MinValue
};
storage.Insert<RegistrationObject>(newApworks.Storage.PropertyBag(registrationObject));
PropertyBagpbUpdateFields=newPropertyBag();
pbUpdateFields.Add("Lent",true);
pbUpdateFields.Add("LendTo",target.ReaderName);
PropertyBagpbCriteria=newPropertyBag();
pbCriteria.Add("AggregateRootId",target.BookId);
storage.Update<BookObject>(pbUpdateFields,pbCriteria);
}
returntrue;
}
}
publicclassBookCreatedEventHandler:Apworks.Events.EventHandler<BookCreatedEvent>
{
publicoverrideboolHandle(BookCreatedEventtarget)
{
using(IQueryObjectStoragestorage=this.GetQueryObjectStorage())
{
BookObjectbookData=newBookObject
{
AggregateRootId=target.AggregateRootId,
ISBN=target.ISBN,
Lent=target.Lent,
Pages=target.Pages,
PubDate=target.PubDate,
Publisher=target.Publisher,
Title=target.Title,
LendTo=string.Empty
};
storage.Insert<BookObject>(newApworks.Storage.PropertyBag(bookData));
}
returntrue;
}
}
publicclassBookReturnedEventHandler:Apworks.Events.EventHandler<BookReturnedEvent>
{
publicoverrideboolHandle(BookReturnedEventtarget)
{
using(IQueryObjectStoragequeryStorage=this.GetQueryObjectStorage())
{
PropertyBagpbCriteria=newPropertyBag();
pbCriteria.Add("BookAggregateRootId",target.BookId);
pbCriteria.Add("ReaderAggregateRootId",target.ReaderId);
PropertyBagpbUpdateFields=newPropertyBag();
pbUpdateFields.Add("ReturnedDate",DateTime.Now);
pbUpdateFields.Add("Returned",true);
queryStorage.Update<RegistrationObject>(pbUpdateFields,pbCriteria);
PropertyBagpbUpdateFieldsBooks=newPropertyBag();
pbUpdateFieldsBooks.Add("Lent",false);
pbUpdateFieldsBooks.Add("LendTo",string.Empty);
PropertyBagpbCriteriaBooks=newPropertyBag();
pbCriteriaBooks.Add("AggregateRootId",target.BookId);
queryStorage.Update<BookObject>(pbUpdateFieldsBooks,pbCriteriaBooks);
}
returntrue;
}
}
publicclassReaderCreatedEventHandler:Apworks.Events.EventHandler<ReaderCreatedEvent>
{
publicoverrideboolHandle(ReaderCreatedEventtarget)
{
using(IQueryObjectStoragestorage=this.GetQueryObjectStorage())
{
ReaderObjectreaderObject=newReaderObject
{AggregateRootId=target.AggregateRootId,LoginName=target.LoginName,Name=target.Name};
storage.Insert<ReaderObject>(newPropertyBag(readerObject));
}
returntrue;
}
}
}
[/code]
事件处理器需要继承Apworks.Events.EventHandler类,并实现Handle方法。在Handle方法中,事件处理器将首先通过GetQueryObjectStorage方法获得查询数据库存储对象,然后通过存储对象来保存查询对象。
至此,我们已经创建了聚合、快照、查询对象、领域事件以及事件处理器。在下一步操作中,我们将会创建一些“业务外观对象”来协调业务处理过程,以便更上层的应用组件能够方便地使用我们的领域模型。在CQRS架构中,这种“业务外观对象”就是命令与命令处理器。
相关文章推荐
- 使用Apworks开发基于CQRS架构的应用程序(二):创建领域模型项目
- 使用Apworks开发基于CQRS架构的应用程序(五):命令
- 使用Apworks开发基于CQRS架构的应用程序(七):配置数据库
- 使用Apworks开发基于CQRS架构的应用程序(九):运行应用程序
- 使用Apworks开发基于CQRS架构的应用程序(六):创建.NET WCF服务
- 使用Apworks开发基于CQRS架构的应用程序(八):应用程序的配置与编译
- 使用Apworks开发基于CQRS架构的应用程序(一):前言
- 使用Apworks开发基于CQRS架构的应用程序(三):创建快照
- Apworks框架实战(四):使用Visual Studio开发面向经典分层架构的应用程序:从EasyMemo案例开始
- 基于Swift的iOS应用程序开发:使用表格显示并控制数据(二):删除表格中的数据
- 基于Swift的iOS应用程序开发:使用表格显示并控制数据(四):使用UIRefreshController实现下拉刷新
- 使用cocoa捕获dock栏中的“退出”事件,解决qt开发的应用程序退出异常的问题
- 基于cepark usb开发板 VS2010使用libusb-win32开发应用程序
- ASP.NET---使用Asp.NET开发三层架构的应用程序
- 基于Swift的iOS应用程序开发:使用xcode进行iOS应用程序开发时,各种Launch Image尺寸清单
- IDDD 实现领域驱动设计-CQRS(命令查询职责分离)和 EDA(事件驱动架构)
- 面向对象编程、面向服务架构、基于组件开发三种编程模式的区别和适用领域
- 基于Swift的iOS应用程序开发:使用UIImagePickerController从相册选择图片
- 如果是作为客户端的HTTP+JSON接口工程,没有JSP等view视图的情况下,使用Jersery框架开发绝对是第一选择。而在基于Spring3 MVC的架构下,对HTTP+JSON的返回类型也有很好
- 使用 PHP 开发基于 Web 服务的应用程序