使用Apworks开发基于CQRS架构的应用程序(五):命令

客户端程序通过命令告知系统“应该做什么”。事实上,这是一种单向的交互过程,客户端程序仅仅向领域模型发送命令请求,它们并不会通过领域模型来查询某些数据信息。在CQRS架构的应用程序中,“查询”是另一部分的内容,这将在接下来的章节中单独讨论。当应用服务器端接收到来自客户端的命令请求后,就会将这些命令推送到命令总线。命令处理器会侦听命令总线,并相应地处理命令请求。现在,让我们在TinyLibraryCQRS解决方案中创建命令与命令解释器。

  1. 右键单击TinyLibraryCQRS解决方案,单击 Add | New Project… 菜单,这将打开Add New Project对话框
  2. Installed Templates选项卡下,选择Visual C# | Windows,然后选择Class Library,确保所选的.NET版本是.NET Framework 4,然后在Name文本框中,输入TinyLibrary.Commands,并单击OK按钮
  3. TinyLibrary.Commands项目上,右键单击References节点,单击Add Reference…菜单,这将打开Add Reference对话框
  4. .NET选项卡下,选择Apworks,然后单击OK按钮
  5. 添加下面的代码:
       1: using System;
       2: using Apworks.Commands;
       3:  
       4: namespace TinyLibrary.Commands
       5: {
       6:     [Serializable]
       7:     public class BorrowBookCommand : Command
       8:     {
       9:         public long ReaderId { get; set; }
      10:         public long BookId { get; set; }
      11:  
      12:         public BorrowBookCommand(long readerId, long bookId)
      13:         {
      14:             this.ReaderId = readerId;
      15:             this.BookId = bookId;
      16:         }
      17:     }
      18:  
      19:     [Serializable]
      20:     public class CreateBookCommand : Command
      21:     {
      22:         public string Title { get; private set; }
      23:         public string Publisher { get; private set; }
      24:         public DateTime PubDate { get; private set; }
      25:         public string ISBN { get; private set; }
      26:         public int Pages { get; private set; }
      27:         public bool Lent { get; private set; }
      28:  
      29:         public CreateBookCommand(string title, 
      30:             string publisher, 
      31:             DateTime pubDate, 
      32:             string isbn, int pages, bool lent)
      33:         {
      34:             this.Title = title;
      35:             this.PubDate = pubDate;
      36:             this.Publisher = publisher;
      37:             this.ISBN = isbn;
      38:             this.Pages = pages;
      39:             this.Lent = lent;
      40:         }
      41:  
      42:         public CreateBookCommand(long id, 
      43:             string title, 
      44:             string publisher, 
      45:             DateTime pubDate, 
      46:             string isbn, int pages, bool lent)
      47:             : base(id)
      48:         {
      49:             this.Title = title;
      50:             this.PubDate = pubDate;
      51:             this.Publisher = publisher;
      52:             this.ISBN = isbn;
      53:             this.Pages = pages;
      54:             this.Lent = lent;
      55:         }
      56:     }
      57:  
      58:     [Serializable]
      59:     public class RegisterReaderCommand : Command
      60:     {
      61:         public string LoginName { get; private set; }
      62:         public string Name { get; private set; }
      63:  
      64:         public RegisterReaderCommand(string loginName, string name)
      65:         {
      66:             this.LoginName = loginName;
      67:             this.Name = name;
      68:         }
      69:  
      70:         public RegisterReaderCommand(long id, string loginName, string name):base(id)
      71:         {
      72:             this.LoginName = loginName;
      73:             this.Name = name;
      74:         }
      75:     }
      76:  
      77:     [Serializable]
      78:     public class ReturnBookCommand : Command
      79:     {
      80:         public long ReaderId { get; set; }
      81:         public long BookId { get; set; }
      82:  
      83:         public ReturnBookCommand(long readerId, long bookId)
      84:         {
      85:             this.ReaderId = readerId;
      86:             this.BookId = bookId;
      87:         }
      88:     }
      89: }

看上去命令类与领域事件类的结构非常相似,的确如此,它们同样是继承于某个基类,同样都应用了System.SerializableAttribute特性。但事实上,命令与领域事件是两种完全不同的语义,虽然在某些情况下,两者结构相似,但这不是必然结果。

命令处理器用来处理已经定义的命令。当整个系统启动的时候,它会将Apworks配置文件里已经定义的命令处理器注册到系统中。有关这个配置文件的具体内容会在后续章节中描述。现在,我们创建一些命令处理器来处理上面已定义的命令。

  1. Solution Explorer中右键单击TinyLibraryCQRS解决方案,然后单击Add | New Project…菜单,这将打开Add New Project对话框
  2. Installed Templates选项卡下,选择Visual C# | Windows,然后选择Class Library,确保选择的.NET版本是.NET Framework 4,然后在Name文本框中,输入TinyLibrary.CommandHandlers,然后单击OK按钮
  3. Solution Explorer中,右键单击TinyLibrary.CommandHandlers项目的References节点,然后选择Add Reference…菜单,这将打开Add Reference对话框
  4. 在.NET选项卡下,选择Apworks然后单击OK按钮
  5. Solution Explorer中,右键单击TinyLibrary.CommandHandlers项目的References节点,然后选择Add Reference…菜单,这将打开Add Reference对话框
  6. Projects选项卡下,选择TinyLibrary.CommandsTinyLibrary.Domain项目,然后单击OK按钮
  7. 向该项目添加以下代码
       1: using Apworks.Commands;
       2: using Apworks.Repositories;
       3: using TinyLibrary.Commands;
       4: using TinyLibrary.Domain;
       5:  
       6: namespace TinyLibrary.CommandHandlers
       7: {
       8:     public class BorrowBookCommandHandler : CommandHandler<BorrowBookCommand>
       9:     {
      10:         public override bool Handle(BorrowBookCommand command)
      11:         {
      12:             using (IDomainRepository repository = this.GetDomainRepository())
      13:             {
      14:                 Reader reader = repository.Get<Reader>(command.ReaderId);
      15:                 Book book = repository.Get<Book>(command.BookId);
      16:                 reader.BorrowBook(book);
      17:                 repository.Save(reader);
      18:                 repository.Save(book);
      19:             }
      20:             return true;
      21:         }
      22:     }
      23:  
      24:     public class CreateBookCommandHandler : CommandHandler<CreateBookCommand>
      25:     {
      26:         public override bool Handle(CreateBookCommand command)
      27:         {
      28:             using (IDomainRepository repository = this.GetDomainRepository())
      29:             {
      30:                 Book book = Book.Create(command.Id, 
      31:                     command.Title, 
      32:                     command.Publisher, 
      33:                     command.PubDate, 
      34:                     command.ISBN, 
      35:                     command.Pages, 
      36:                     command.Lent);
      37:                 repository.Save<Book>(book);
      38:             }
      39:             return true;
      40:         }
      41:     }
      42:  
      43:     public class RegisterReaderCommandHandler : CommandHandler<RegisterReaderCommand>
      44:     {
      45:         public override bool Handle(RegisterReaderCommand command)
      46:         {
      47:             using (IDomainRepository repository = this.GetDomainRepository())
      48:             {
      49:                 Reader reader = Reader.Create(command.Id, command.LoginName, command.Name);
      50:                 repository.Save(reader);
      51:             }
      52:             return true;
      53:         }
      54:  
      55:     }
      56:  
      57:     public class ReturnBookCommandHandler : CommandHandler<ReturnBookCommand>
      58:     {
      59:         public override bool Handle(ReturnBookCommand command)
      60:         {
      61:             using (IDomainRepository repository = this.GetDomainRepository())
      62:             {
      63:                 Reader reader = repository.Get<Reader>(command.ReaderId);
      64:                 Book book = repository.Get<Book>(command.BookId);
      65:                 reader.ReturnBook(book);
      66:                 repository.Save(reader);
      67:                 repository.Save(book);
      68:             }
      69:             return true;
      70:         }
      71:     }
      72: }

从上面的代码我们可以看到,所有的命令处理器都从CommandHandler泛型抽象类继承而来,同时实现其Handle方法。通常,在Handle方法中,命令处理器会根据命令的具体内容,通过领域仓储来获得或更新聚合。要得到领域仓储的实例,可以使用定义在CommandHandler基类中的GetDomainRepository方法。建议在GetDomainRepository的调用端使用using子句以便及时释放资源。

就如我们上面讨论的那样,每当系统得到一个命令时,都将把它推送到命令总线中。当总线被提交的时候,已注册的命令处理器会处理与之对应的命令请求。现在,我们需要创建一个应用级的服务外观来接收客户端命令请求。在TinyLibraryCQRS解决方案中,这个应用服务外观被定义成了一个.NET WCF服务。下一讲将介绍这个.NET WCF服务的创建过程。

原文地址:https://www.cnblogs.com/daxnet/p/1954341.html