云计算设计模式(六)——命令和查询职责分离(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 方法可以更好地定制,以满足该组织开展业务及库存管理的方式。

相关文章: 

  • 云计算设计模式(一)缓存预留模式

  • 云计算设计模式(二)——断路器模式

  • 云计算设计模式(三)——补偿交易模式

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

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

原文地址:http://blog.csdn.net/yangzhenping/article/details/40818331


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/326967.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Intellij IDEA 那些隐藏好用的小技巧

转载自 Intellij IDEA 那些隐藏好用的小技巧 概述 之前写了一篇介绍IntellIJ IDEA的文章《 Intellij Idea非常6的10个姿势 》&#xff0c;主要是列出一些平时大家可能没用过或者没怎么用&#xff0c;但是又非常好用的IntellIJ IDEA小技巧。由于篇幅原因&#xff0c;只是列出了…

约瑟夫(环)问题(Josephu)(单向环形链表)

问题描述 代码实现 package com.atguigu.linkedlist;import com.sun.org.apache.bcel.internal.generic.NEW;/*** 创建人 wdl* 创建时间 2021/3/19* 描述*/ public class Josepfu {public static void main(String[] args) {//测试一把看看构建的环形链表和遍历是否正确Circle…

vue+vscode+nodejs 开发环境搭建

参考文献 vuevscodenodejs 开发环境搭建 - Desperador - 博客园 nodejs 指定全局安装路径和缓存路径 - Curedfisher - 博客园 安装配置nodejs并创建Vue项目 vscode下载地址&#xff1a; Documentation for Visual Studio Code nodejs安装配置 1.下载 地址&#xff1a; …

用数组模拟栈

思路分析 代码实现 package com.atguigu.stack;import com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM2;import java.net.ServerSocket; import java.util.ArrayList; import java.util.Scanner; import java.util.Stack;/*** 创建人 wdl* 创建时间 2021/3/20* 描述…

Redis PK Memcached,哪个更牛叉

转载自 Redis PK Memcached&#xff0c;哪个更牛叉 说到 redis 就会联想到 memcached&#xff0c;反之亦然。了解过两者的同学有那么个大致的印象&#xff1a; redis 与 memcached 相比&#xff0c;不仅支持简单的 key-value 数据类型&#xff0c;同时还提供 list,set,zset,ha…

CoreCLR源码探索(三) GC内存分配器的内部实现

在前一篇中我讲解了new是怎么工作的, 但是却一笔跳过了内存分配相关的部分.在这一篇中我将详细讲解GC内存分配器的内部实现.在看这一篇之前请必须先看完微软BOTR文档中的"Garbage Collection Design",原文地址是: https://github.com/dotnet/coreclr/blob/master/Doc…

vue学习1

P1 01_Vue学习目标03:50 P2 02_前端知识体系16:27 P3 03_前后端分离的演变史17:13 P4 04_前端MVVM模式09:31 P5 05_Vue是什么07:23 P6 06_第一个Vue应用程序07:06 P7 07_Vue实例声明周期05:35 P8 08_条件渲染06:59 P9 09_列表渲染03:34 P10 10_事件处理03:44…

Tomcat 的 Server 文件配置详解

转载自 Tomcat 的 Server 文件配置详解 前言 Tomcat隶属于Apache基金会&#xff0c;是开源的轻量级Web应用服务器&#xff0c;使用非常广泛。server.xml是Tomcat中最重要的配置文件&#xff0c;server.xml的每一个元素都对应了Tomcat中的一个组件&#xff1b;通过对xml文件中…

.Net基础体系和跨框架开发普及

.net体系经过十几年发展&#xff0c;发生了很多变化。特别是在最近两年&#xff0c;随着开源和跨平台的发展&#xff0c;衍生出很多概念&#xff0c;像标准库&#xff0c;可移植库&#xff0c;.Net Core等&#xff0c;相信有不少同学对他们之间的关系是有一些困惑的&#xff0c…

‘1‘ VS 1

‘1’-481; 把字符转换为数字&#xff0c;利用ASCALL表

一次恐怖的 Java 内存泄漏排查实战

转载自 一次恐怖的 Java 内存泄漏排查实战 最近在看《深入理解Java虚拟机&#xff1a;JVM高级特性与最佳实践》&#xff08;第二版&#xff09;这本书&#xff0c;理论实践结合&#xff0c;深入浅出&#xff0c;强烈推荐给大家。 这两天对JVM内容进行了一个讨论&#xff0c;…

ASP.NET与ASP.NET Core用户验证Cookie并存解决方案

在你将现有的用户登录&#xff08;Sign In&#xff09;站点从ASP.NET迁移至ASP.NET Core时&#xff0c;你将面临这样一个问题——如何让ASP.NET与ASP.NET Core用户验证Cookie并存&#xff0c;让ASP.NET应用与ASP.NET Core应用分别使用各自的Cookie&#xff1f;因为ASP.NET用的是…

vue学习2

P1 01_Vue学习目标03:50 P2 02_前端知识体系16:27 P3 03_前后端分离的演变史17:13 P4 04_前端MVVM模式09:31 P5 05_Vue是什么07:23 P6 06_第一个Vue应用程序07:06 P7 07_Vue实例声明周期05:35 P8 08_条件渲染06:59 P9 09_列表渲染03:34 P10 10_事件处理03:44…

一道非常棘手的 Java 面试题:i++ 是线程安全的吗

转载自 一道非常棘手的 Java 面试题&#xff1a;i 是线程安全的吗 i 是线程安全的吗&#xff1f; 相信很多中高级的 Java 面试者都遇到过这个问题&#xff0c;很多对这个不是很清楚的肯定是一脸蒙逼。内心肯定还在质疑&#xff0c;i 居然还有线程安全问题&#xff1f;只能说…

Microsoft规划了.NET的未来发展

Microsoft的Mads Torgersen分享了.NET语言家族的更新策略&#xff0c;给出了对公司未来的功能考虑的深刻理解。虽然C#、VB.NET和F#的开发是通过GitHub公开进行的&#xff0c;但是Microsoft的长远规划却经常是保密的。公众如果对Microsoft目前思考问题的方式有相关的意见和建议的…

逆波兰计算器实现

逆波兰计算器 思路分析 代码实现 package com.atguigu.stack;import java.security.AlgorithmConstraints; import java.util.ArrayList; import java.util.List; import java.util.Stack;/*** 创建人 wdl* 创建时间 2021/3/20* 描述*/ public class PolandNotation {public …

逆波兰表达式中缀表达式转换为后缀表达式

中缀表达式转换为后缀表达式 思路分析 代码实现 package com.atguigu.stack;import javax.swing.plaf.nimbus.State; import java.security.AlgorithmConstraints; import java.util.ArrayList; import java.util.List; import java.util.Stack;/*** 创建人 wdl* 创建时间 20…

OSS.Common扩展.Net Standard支持实例分享

上篇&#xff08;.Net基础体系和跨框架开发普及&#xff09;介绍了.Net当前生态下的大概情况&#xff0c;也分享了简单实现的过程&#xff0c;这篇文章就是讲解我的OSS.Common项目扩展.Net Standard 支持的过程&#xff0c;主要集中在&#xff1a;方案的选择&#xff0c;移植检…