云计算设计模式(六)——命令和查询职责分离(CQRS)模式

云计算设计模式(六)——命令和查询职责分离(CQRS)模式

隔离,通过使用不同的接口,从操作读取数据更新数据的操作。这种模式可以最大限度地提高性能,可扩展性和安全性;支持系统在通过较高的灵活性,时间的演变;防止更新命令,从造成合并在域级别上的冲突。

背景和问题

在传统的数据管理系统中,这两个命令(更新数据)和查询(请求数据),针对在一个单一的数据存储库中的相同的一组实体的执行。这些实体可以是在关系数据库中的一个或多个表,如SQL
Server的行的子集。

典型地,在这些系统中,所有的创建,读取,更新和删除(CRUD)操作被施加到该实体的相同的表示。例如,一个数据传输对象(DTO)的代表顾客从数据存储中检索由数据访问层(DAL)并显示在屏幕上。用户更新DTO的某些领域(也许是通过数据绑定)和DTO,然后保存回数据存储在DAL。相同的DTO同时用于读取和写入操作,如图1所示。

图1 - 一个传统的CRUD架构

传统的CRUD设计工作良好时,只有施加到数据操作有限的业务逻辑。由开发工具提供可以非常快速地创建数据访问代码的支架机构,根据需要,可再进行定制。

然而,传统的CRUD方法有一些缺点:

?它往往意味着存在所述读取和写入的数据,如额外的列或属性,即使它们不是必需的作为操作的一部分,必须正确地更新的表示之间的不匹配。

?它遇到风险的数据争用一个协作领域(在多个参与者??并行运行在相同的数据集)时,记录被锁定在数据存储,或者更新冲突所造成的并发更新时,乐观锁使用。这些风险增加的复杂性和系统的吞吐量增加。此外,传统的方法也可以对性能有负面影响,由于加载的数据存储和数据访问层上,并在检索信息需要查询的复杂度。

?它可以使安全管理和权限比较繁琐,因为每一个实体是受读取和写入操作,这可能会在不经意间暴露的数据在错误的情况下。

注意:

对于的CRUD方法的局限性有了更深的了解请参见“CRUD,只有当你能负担得起”MSDN上。

解决方案

命令和查询职责分离(CQRS)是偏析,通过使用独立的接口读取操作的更新数据(命令)的数据(查询)的操作模式。这意味着,用于查询和更新的数据模型是不同的。该模型可随后被分离,如在图2中,虽然这不是绝对的要求。

图2 - 一个基本的CQRS架构

相比于数据(从该开发商建立自己的概念模式)的单个模型中固有的CRUD为基础的系统中,使用单独的查询和更新模型中CQRS为基础的系统中的数据显着地简化设计和实施。然而,一个缺点是,不像CRUD的设计,CQRS代码不能自动用支架的机制产生。

查询模型读取数据和写入数据可以访问相同的实体店,也许是通过使用SQL视图的更新模型,或产生对飞预测。但是,它是常见的数据分成不同的物理存储来提高性能,可扩展性和安全性;如图3。

图3 - 一个CQRS架构,具有独立读写店

所读取的存储可以是只读副本写入存储区,或读取和写入存储可以具有不同的结构完全。使用read店的多个只读副本可以大大提高查询性能和应用程序的UI响应速度,尤其是在分布式场景下的只读副本靠近应用程序实例。一些数据库系统,如SQL
Server,提供额外的功能,如故障转移副本,以最大限度地提高可用性。

读的分离和写入存储还允许每个到会适当缩放以匹配负载。例如,读取存储通常会遇到一个更高的负载写入存储。

当查询/读取模型中包含的非规范化的信息(见物化视图模式),性能正在读取数据的每一个视图时在应用程序中或在查询系统中的数据时最大化。

有关CQRS模式及其实现的详细信息,请参阅以下资源:

?该模式与实践指导CQRS之旅MSDN上。尤其是你应该阅读的章节介绍了命令查询职责分离方式进行全面的探索模式,当它是有用的,这一章尾声:经验教训,了解一些,可以使用这种模式时出现的问题。

?该职位CQRS由马丁·福勒,这也解释了该模式的基本知识,并链接到其他一些有用的资源。

?代码更好的网站,它探讨的CQRS模式的许多方面对Greg Young的帖子。

问题和注意事项

在决定如何实现这个模式时,请考虑以下几点:

?分割数据存储到单独的物理存储用于读操作和写操作可以提高系统的性能和安全性,但它可以在弹性和最终一致性方面增加了相当大的复杂性。所读取的模型存储必须被更新以反映变化的写入模型存储,并且它可以是难以检测用户何时发出基于读取过时数据意味着该操作不能完成的请求。

注意:

对于最终一致性的说明,请参阅数据一致性底漆。

?考虑CQRS应用到你的系统中的限制部分地方将是最有价值的,并从经验中学习。

?的典型方法拥抱最终一致性是使用事件采购与CQRS结合使写模式是由执行命令的驱动事件的追加只流。这些事件被用来更新充当读取模型化视图。欲了解更多信息,请参阅事件获取和CQRS。

当使用这个模式

这种模式非常适合于:

?其中并行地对相同的数据进行多项操作协同域。
CQRS允许你有足够的粒度定义的命令,以尽量减少在域级别(或者不出现可以通过在命令合并的冲突)的合并冲突,更新这似乎是同一类型的数据时也是如此。

?使用与基于任务的用户界面(其中用户通过一个复杂的过程引导作为一系列步骤),具有复杂的领域模型,以及用于团队已经熟悉领域驱动设计(DDD)技术。在写入模式有一个完整的命令处理栈与业务逻辑,输入验证和业务验证,以确保一切总是为每个聚集体(被视为一个单元进行数据变更的目的相关联的对象的每个集群相一致)中的写入模式。读出的模型没有业务逻辑或验证的堆栈,只是返回一个DTO在一个视图模型的使用。读出的模型与模型写入最终一致。

?方案,其中数据的读出性能,必须分别从数据的性能进行微调写入,尤其是当读/写比是非常高的,并且当水平扩展是必要的。例如,在许多系统中的读取操作的数目是几个数量级更大的写入操作的数目。为了适应这种情况,考虑向外扩展的读取模式,但只在一个或几个实例中运行的写模式。少数写入模型实例也有助于减少合并冲突的发生。

?场景的开发者之一的团队可以专注于复杂的领域模型,它是写模型的一部分??,而另一个经验不足的团队可以专注于读模型和用户界面。

?场景中,预计随着时间的推移,系统,并且可以包含多个版本的模型,或者业务规则经常改变。

?与其他系统,特别是与事件采购,其中一个子系统的瞬时故障不会影响到其它的可用性的组合一体化。

这种模式可能不适合于下列情况:

?凡域或业务规则很简单。

?凡一个简单的CRUD风格的用户界面和相关的数据访问操作就足够了。

?对于在整个系统中的实现。有一个整体的数据管理方案,其中CQRS可以是有用的特定组件,但是它可以增加它实际上并不需要相当大的和往往是不必要的复杂性。

事件获取和CQRS

CQRS模式常用于与事件获取图案一起使用。
CQRS为基础的系统使用分离的读取和写入的数据模型,每个针对有关任务和通常位于物理上分离的存储区。当与采购活动时,事件的存储是写模式,这是信息的权威来源。一个CQRS为基础的系统的读取模型提供数据的物化视图,通常是高度非规范化的意见。这些视图量身定做的接口和应用程序,这有助于最大程度地显示和查询性能的显示要求。

使用事件作为写入存储区,而不是实际的数据的流,在一个时间点,避免了在单个聚合更新冲突并最大限度地提高性能和可扩展性。该事件可用于异步生成用于填充读取存储器中的数据的实体化视图。

由于事件存储是信息的权威来源,就可以删除物化视图和回放所有过去的事件来创建当前状态的一个新表示当系统升级时,或者当读取模式必须改变。物化视图是有效的数据的耐用只读缓存。

当使用CQRS结合事件获取模式,考虑以下几点:

?与任何系统,其中写入和读出存储是分开的,在此基础上图案系统唯一最终一致。将有被生成的事件和数据存储器保存由这些事件被更新启动操作的结果之间有一些延迟。

?该模式引入由于代码必须创建启动和处理事件,并组装或者更新查询或读取模型所需的适当的意见或物体额外的复杂性。在采购活动一起使用的CQRS模式固有的复杂性时,可以做一个成功的实现更加困难,需要重新学习的一些概念和不同的方法来设计系统。然而,事件采购可以更容易地对域进行建模,并且可以更容易地重建的观点或创建新的,因为变化的数据的意图将被保留。

?生成物化视图中读取模型或数据通过重放和处理为特定的实体或实体的集合的事件突起的使用可能需要相当多的处理时间和资源的使用,尤其是如果它需要求和或值的数据在长时间内的,因为所有的相关联的事件可能需要被审查。这可以通过实现数据的快照在预定的时间间隔,如已经发生的特定操作的次数,或一个实体的当前状态的总计数被部分地解决。

注意:

欲了解更多信息,请参阅活动采购模式和物化视图模式,以及模式与实践指导CQRS之旅MSDN上。尤其是你应该阅读的章节介绍采购活动进行全面的探索模式,以及它如何与CQRS有用的,而章CQRS和ES深潜了解更多,包括如何聚集分区可以在微软的Azure
CQRS使用。

例子

下面的代码显示了一个CQRS实现,它使用不同的定义读取和写入模型为例某些提取物。该模型的接口没有规定的基础数据存储的任何功能,并且可以发展和进行微调独立,因为这些接口是分开的。

下面的代码演示了读取的模型定义。

// Query interface
namespace ReadModel
{
  public interface ProductsDao
  {
    ProductDisplay FindById(int productId);
    IEnumerable<ProductDisplay> FindByName(string name);
    IEnumerable<ProductInventory> FindOutOfStockProducts();
    IEnumerable<ProductDisplay> FindRelatedProducts(int productId);
  }

  public class ProductDisplay
  {
    public int ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal UnitPrice { get; set; }
    public bool IsOutOfStock { get; set; }
    public double UserRating { get; set; }
  }

  public class ProductInventory
  {
    public int ID { get; set; }
    public string Name { get; set; }
    public int CurrentStock { get; set; }
  }
}

该系统允许用户率的产品。应用程序代码通过使用在下面的代码中所示的RateProduct命令执行此操作。

<span></span><pre class="csharp" name="code">public interface Icommand
{
  Guid Id { get; }
}

public class RateProduct : Icommand
{
  public RateProduct()
  {
    this.Id = Guid.NewGuid();
  }
  public Guid Id { get; set; }
  public int ProductId { get; set; }
  public int rating { get; set; }
  public int UserId {get; set; }
}

本系统采用ProductsCommandHandler类来处理由应用程序发出的命令。客户端通常通过消息传送系统发送命令到域,如一个队列。命令处理程序接受这些命令,并调用域接口的方法。每个命令的粒度被设计成减轻冲突请求的机会。下面的代码显示了ProductsCommandHandler类的轮廓。

public class ProductsCommandHandler :
    ICommandHandler<AddNewProduct>,
    ICommandHandler<RateProduct>,
    ICommandHandler<AddToInventory>,
    ICommandHandler<ConfirmItemShipped>,
    ICommandHandler<UpdateStockFromInventoryRecount>
{
  private readonly IRepository<Product> repository;

  public ProductsCommandHandler (IRepository<Product> repository)
  {
    this.repository = repository;
  }

  void Handle (AddNewProduct command)
  {
    ...
  }

  void Handle (RateProduct command)
  {
    var product = repository.Find(command.ProductId);
    if (product != null)
    {
      product.RateProuct(command.UserId, command.rating);
      repository.Save(product);
    }
  }

  void Handle (AddToInventory command)
  {
    ...
  }

  void Handle (ConfirmItemsShipped command)
  {
    ...
  }

  void Handle (UpdateStockFromInventoryRecount command)
  {
    ...
  }
}

下面的代码显示了写模式ProductsDoman接口。

public interface ProductsDomain
{
  void AddNewProduct(int id, string name, string description, decimal price);
  void RateProduct(int userId int rating);
  void AddToInventory(int productId, int quantity);
  void ConfirmItemsShipped(int productId, int quantity);
  void UpdateStockFromInventoryRecount(int productId, int updatedQuantity);
}

还要注意如何ProductsDomain接口包含在域中的意义的方法。通常情况下,在一个CRUD环境中,这些方法将有通用名称,如保存或更新,并有一个DTO作为唯一的参数。该CQRS方法可以更好地定制,以满足该组织开展业务及库存管理的方式。

本文翻译自MSDN:http://msdn.microsoft.com/en-us/library/dn568103.aspx

时间: 2024-10-21 12:23:16

云计算设计模式(六)——命令和查询职责分离(CQRS)模式的相关文章

浅谈命令查询职责分离(CQRS)模式

在常用的三层架构中,通常都是通过数据访问层来修改或者查询数据,一般修改和查询使用的是相同的实体.在一些业务逻辑简单的系统中可能没有什么问题,但是随着系统逻辑变得复杂,用户增多,这种设计就会出现一些性能问题.虽然在DB上可以做一些读写分离的设计,但在业务上如果在读写方面混合在一起的话,仍然会出现一些问题. 本文介绍了命令查询职责分离模式(Command Query Responsibility Segregation,CQRS),该模式从业务上分离修改 (Command,增,删,改,会对系统状态进

转:浅谈命令查询职责分离(CQRS)模式

原文来自于:http://www.cnblogs.com/yangecnu/p/Introduction-CQRS.html 在常用的三层架构中,通常都是通过数据访问层来修改或者查询数据,一般修改和查询使用的是相同的实体.在一些业务逻辑简单的系统中可能没有什么问题,但是随着系统逻辑变得复杂,用户增多,这种设计就会出现一些性能问题.虽然在DB上可以做一些读写分离的设计,但在业务上如果在读写方面混合在一起的话,仍然会出现一些问题. 本文介绍了命令查询职责分离模式(Command Query Resp

云计算设计模式(十九)——运行重构模式

云计算设计模式(十九)——运行重构模式 设计应用程序,使得它可以在不需要重新部署或者重新启动应用程序重新配置.这有助于保持可用性并减少停机时间. 背景和问题 一个主要目的为重要的应用,如商业和企业网站是尽量减少停机时间以及由此引发的中断给客户和用户.但是,有时有必要重新配置应用程序改变特定行为或设置,而在部署和使用.因此,它是用于该应用程序被设计成这样一种方式,以允许在运行时要应用这些配置的变化,并为应用程序,以检测所述变化并且尽快地应用它们的部件的优点. 该种要应用可能被调整记录,以协助与应用

云计算设计模式(十一)——健康端点监控模式

云计算设计模式(十一)——健康端点监控模式 实施外部工具可以定期通过暴露终端访问应用程序中的功能检查.这个模式可以帮助验证的应用和服务被正确执行 背景和问题 它是很好的做法,并且通常是一个业务需求,并监控web应用程序,和中间层和共享服务,以确保它们是可用的,并执行正确的.然而,它更难以监测在云中运行比它要监控本地服务的服务.举例来说,你不必完全控制主机环境,而服务通常依赖于平台,供应商和其他公司提供其他服务. 也有一些影响云托管的应用,如网络延迟,性能和下面的计算和存储系统的可用性,以及它们之

云计算设计模式(五)——计算资源整合模式

云计算设计模式(五)--计算资源整合模式 合并多个任务或操作成一个单一的计算单元.这种模式可以提高计算资源的利用率,并降低与云托管的应用程序进行计算处理相关的成本和管理开销. 背景和问题 云应用程序频繁执行各种操作.在某些解决方案也可能是有意义的最初遵循的关注点分离的设计原则,并把这些操作成托管和独立部署(例如,如在微软的Azure云服务,独立Azure网站不同的角色独立计算单元或单独的虚拟机).然而,尽管这种策略可以帮助简化溶液的逻辑设计,部署大量计算单元作为同一应用可以提高运行时的托管成本,

云计算设计模式(十九)——执行重构模式

云计算设计模式(十九)--执行重构模式 设计应用程序,使得它能够在不须要又一次部署或者又一次启动应用程序又一次配置.这有助于保持可用性并降低停机时间. 背景和问题 一个主要目的为重要的应用.如商业和企业站点是尽量降低停机时间以及由此引发的中断给客户和用户. 可是.有时有必要又一次配置应用程序改变特定行为或设置,而在部署和使用.因此,它是用于该应用程序被设计成这样一种方式,以同意在执行时要应用这些配置的变化,并为应用程序.以检測所述变化而且尽快地应用它们的部件的长处. 该种要应用可能被调整记录,以

云计算设计模式(八)——外部配置存储模式

云计算设计模式(八)--外部配置存储模式 移动配置信息从应用部署包到一个集中位置.这个模式可以提供机会,以便管理和配置数据的控制,以及用于跨应用程序和应用程序实例共享的配置数据. 背景和问题 大多数应用程序运行时环境包括位于应用程序文件夹内的在部署应用程序文件保持配置信息.在某些情况下也能够编辑这些文件来改变该应用程序的行为,它已经被部署之后.然而,在许多情况下,改变配置所需要的应用程序被重新部署,从而导致不可接受的停机时间和额外的管理开销. 本地配置文件还配置限制为单个应用程序,而在某些情况下

云计算设计模式(二十三)——Throttling节流模式

云计算设计模式(二十三)——Throttling节流模式 控制由应用程序使用,一个单独的租户或整个服务的一个实例的资源的消耗.这种模式可以允许系统继续运行并满足服务水平协议,即使当增加需求的资源放置一个极端载荷. 背景和问题 在云应用负载通常上变化的基础上的活动用户的数量或他们正在执行的活动类型的时间.例如,多个用户可能会在工作时间被激活,否则系统可能被要求在每月结束时执行计算昂贵的分析.也有可能是突然和意外的突发活动.如果系统的处理要求超过了可用的资源的能力,其将遭受性能不佳,甚至会失败.该系

云计算设计模式(四)——消费者的竞争模式

云计算设计模式(四)--消费者的竞争模式 允许多个并发用户处理在同一个通讯通道接收的消息.这种模式使系统能够同时处理多个邮件,以优化吞吐量,提高可扩展性和可用性,以及平衡工作负载. 背景和问题 在云中运行的应用程序,可以预计,以处理大量的请求.而不是过程的每个请求同步地,一个常用的方法是通过一个消息传送系统到该异步地处理它们的另一服务(消费者服务),以通过他们的应用程序.这种策略有助于确保在应用程序的业务逻辑没有被阻塞,而正在处理的请求. 请求的数量可以随着时间的原因有很多显著变化.突然一阵在用