Questions tagged «cqrs»

命令查询职责隔离设计模式


5
REST API如何适合基于命令/操作的域?
在本文中,作者声称 有时,需要公开API中固有的非RESTful操作。 然后 如果一个API有太多的动作,则表明它是使用RPC观点设计的,而不是使用RESTful原理设计的,或者所讨论的API自然更适合RPC类型模型。 这也反映了我在其他地方阅读和听到的内容。 但是,我觉得这很令人困惑,我希望对此事有更好的了解。 示例I:通过REST接口关闭VM 我认为,有两种根本不同的方法来对虚拟机关闭建模。每种方式可能会有一些变化,但现在让我们集中讨论最基本的差异。 1.修补资源的状态属性 PATCH /api/virtualmachines/42 Content-Type:application/json { "state": "shutting down" } (或者,PUT在子资源上/api/virtualmachines/42/state。) VM将在后台关闭,并且在稍后的某个时间点(取决于关闭的成功与否)将成功,否则状态可能不会通过“关闭电源”在内部进行更新。 2.在资源的actions属性上进行PUT或POST PUT /api/virtualmachines/42/actions Content-Type:application/json { "type": "shutdown" } 结果与第一个示例完全相同。该状态将立即更新为“关闭”,并可能最终更新为“关闭电源”。 两种设计都是RESTful的吗? 哪个设计更好? 示例II:CQRS 如果我们有一个CQRS域,其中包含许多这样的“操作”(又称命令),它们可能潜在地导致多个聚合的更新,或者无法映射到具体资源和子资源上的CRUD操作? 我们是否应该在可能的情况下尝试建模尽可能多的命令,具体取决于在具体资源上创建或更新的具体资源(遵循示例I的第一种方法),其余部分使用“动作端点”? 还是应该将所有命令映射到动作端点(如示例I的第二种方法)? 我们应该在哪里划界线?设计何时变得不那么RESTful? CQRS模型是否更适合RPC之类的API? 据我了解,根据上面引用的文字。 从我的许多问题中可以看出,我对该主题有些困惑。您能帮我更好地了解它吗?

2
如何准确地验证CQRS命令并将其转换为域对象?
我一直在修改穷人的CQRS 1,因为我喜欢它的灵活性,可以在一个数据存储中存储细粒度的数据,这为分析提供了极大的可能性,从而增加了商业价值,并且在需要时还提供了另一种包含非规范化数据的读取以提高性能。 。 但是不幸的是,从一开始,我就一直在为应该在这种架构中放置业务逻辑的问题而苦苦挣扎。 据我了解,命令是传达意图的手段,它本身与域没有关系。它们基本上是数据(哑巴,如果需要的话)传输对象。这是为了使命令可以在不同技术之间轻松转移。对于成功完成的事件的响应,同样适用于事件。 在典型的DDD应用程序中,业务逻辑驻留在实体,值对象,集合根中,它们既包含数据又包含行为。但是命令不是域对象,因此命令不应仅限于数据的域表示形式,因为这会对它们造成很大的压力。 因此,真正的问题是:逻辑到底在哪里? 我发现我在尝试构建一个非常复杂的集合(该集合为它的值的组合设置一些规则)时往往最容易遇到这种斗争。另外,在对域对象建模时,我喜欢遵循快速失败范例,知道对象何时到达处于有效状态的方法。 假设聚合Car使用两个组件: Transmission, Engine。 两个Transmission和Engine值对象被表示为超级类型和具有根据子类型,Automatic和Manual传输,或Petrol与Electric分别引擎。 在此域中,完全依靠成功创建的Transmission,,Automatic或Manual或任意一种类型来生存Engine。但是Car聚合引入了一些新规则,这些规则仅在Transmission和Engine对象在同一上下文中使用时才适用。即: 当汽车使用Electric发动机时,唯一允许的变速箱类型是Automatic。 当汽车使用Petrol发动机时,可能有两种类型的发动机Transmission。 我可以在创建命令的级别上捕获这种违反组件组合的行为,但是正如我之前所说,据我所知,不应这样做,因为命令将包含业务逻辑,而业务逻辑应限于域层。 一种选择是将这种业务逻辑验证移至命令验证器本身,但这似乎也不对。感觉就像我将解构该命令,检查使用getter检索的属性,并在验证器中比较它们并检查结果。这让我尖叫得像是违反了得墨meter耳的法律。 放弃提到的验证选项,因为它似乎不可行,似乎应该使用该命令并从中构造聚合。但是这种逻辑应该在哪里存在?是否应该在负责处理具体命令的命令处理程序中?还是应该在命令验证器中(我也不喜欢这种方法)? 我当前正在使用命令,并在负责的命令处理程序中从中创建一个聚合。但是,当我执行此操作时,如果我有一个命令验证器,它将根本不包含任何内容,因为如果该CreateCar命令存在,则它将包含我知道在单独的情况下有效的组件,但集合可能表示不同。 让我们想象一个混合了不同验证过程的场景-使用CreateUser命令创建一个新用户。 该命令包含Id将要创建的一个用户及其Email。 系统为用户的电子邮件地址规定以下规则: 必须是唯一的 不能为空, 最多包含100个字符(数据库列的最大长度)。 在这种情况下,即使有一个唯一的电子邮件是一条业务规则,但对其进行汇总检查几乎没有任何意义,因为我需要将系统中的全部当前电子邮件加载到内存中,并在命令中检查该电子邮件(Eeeek!某物,某物,性能)。因此,我将此检查移至命令验证程序,该程序将UserRepository作为依赖项,并使用存储库来检查是否存在使用命令中存在电子邮件的用户。 当涉及到这一点时,突然将其他两个电子邮件规则也放入命令验证器中是有意义的。但是我觉得规则应该确实存在于User聚合中,并且命令验证程序应该只检查唯一性,如果验证成功,我应该继续在中创建User聚合CreateUserCommandHandler并将其传递到要保存的存储库中。 我之所以这样,是因为存储库的save方法很可能会接受一个聚合,该聚合可确保一旦传递了聚合,所有不变量都将得到满足。当逻辑(例如,非空性)仅出现在命令验证本身中时,另一位程序员可以完全跳过此验证,并直接UserRepository使用User对象调用对象中的save方法,这可能导致致命的数据库错误,因为电子邮件可能包含太久了。 您个人如何处理这些复杂的验证和转换?我大多对自己的解决方案感到满意,但是我觉得我需要肯定的是,我对自己的选择并不满意,但我的想法和方法并不完全愚蠢。我完全接受完全不同的方法。如果您有自己尝试过的东西并且为您做得很好,我很乐意看到您的解决方案。 1作为负责创建RESTful系统的PHP开发人员,我对CQRS的解释与标准的异步命令处理方法略有不同,例如有时由于需要同步处理命令而从命令返回结果。

2
ES / CQRS并发处理
我最近开始研究CQRS / ES,因为我可能需要在工作中应用它。对于我们来说,这似乎很有希望,因为它将解决很多问题。 我对ES / CQRS应用程序如何看起来像上下文简化后的银行业务用例(提取资金)进行了粗略的了解。 综上所述,如果A人提取了一些钱: 发出命令 命令已交出以进行验证/验证 如果验证成功,则将事件推送到事件存储 聚合器使事件出队以对聚合应用修改 据我了解,事件日志是事实的源泉,因为它是FACTS的日志,因此我们可以从中得出任何预测。 现在,在这种宏伟的计划中,我不了解的是在这种情况下会发生什么: 规则:余额不能为负 A人的余额为100e 人A发出100e的WithdrawCommand 验证通过并发出100e事件的MoneyWithdrewEvent 同时,人A发出另一个100e的WithdrawCommand 第一个MoneyWithdrewEvent尚未汇总,因此验证通过,因为针对汇总(尚未更新)的验证检查 再次发出100e的MoneyWithdrewEvent ==>我们处于不一致状态,余额为-100e,并且日志包含2 MoneyWithdrewEvent 据我了解,有几种策略可以解决此问题: a)将聚合版本ID和事件一起放入事件存储中,因此,如果修改后版本不匹配,则不会发生任何事情 b)使用一些锁定策略,这意味着验证层必须以某种方式创建一个 与策略有关的问题: a)在这种情况下,事件日志不再是事实的源头,如何处理?另外,我们还给客户端确定,但是允许退出是完全错误的,在这种情况下使用锁是否更好? b)锁==死锁,您对最佳做法有什么见解? 总体而言,我对如何处理并发的理解正确吗? 注意:我了解同一个人不可能在如此短的时间内提取两次资金,但是我举了一个简单的例子,不要迷失细节

3
如何处理命令中的验证后错误(DDD + CQRS)
例如,当您提交注册表单时,您必须在Domain Model(WriteModel中CQRS)中检查表单是否处于有效状态(例如,电子邮件地址语法,年龄等)。 然后,您创建一个Command,并将其发送到Command Bus。 我知道Commands不应该返回任何东西。 那么,您如何处理超出范围的错误Command Bus呢?(例如,用户在1秒钟之前使用进行了注册username/email)。 您如何知道该命令失败,以及如何知道该错误?

5
服务在SOA中共享数据库是不好的做法吗?
我最近一直在阅读Hohpe和Woolf的企业集成模式,Thomas Erl的一些关于SOA的书,以及观看Udi Dahan等人的各种视频和播客。在CQRS和事件驱动系统上。 我工作场所中的系统耦合度很高。尽管理论上每个系统都有其自己的数据库,但是它们之间有很多连接。实际上,这意味着所有系统都使用一个庞大的数据库。例如,只有一张客户数据表。 我读过的大部分内容似乎都建议对数据进行非规范化处理,以便每个系统仅使用其数据库,并且使用消息传递将对一个系统的任何更新传播到所有其他系统。 我认为这是在SOA中强制执行边界的一种方法-每个服务都应该有自己的数据库,但是我读到以下内容: /programming/4019902/soa-joining-data-across-multiple-services 这表明这是错误的做法。 隔离数据库似乎是解耦系统的一种好方法,但是现在我有点困惑。这是一条好路吗?是否曾经建议过在SOA服务,DDD绑定上下文,应用程序等上隔离数据库?

3
使用DDD和CRQS时,每个命令应该恰好是一个事件吗?
我正在寻找一种设计约定通用的ddd应用程序的方法。 说一个聚合“客户端”有一个定义为“ FillProfile”的命令。它将在逻辑上引发事件“ ProfileFilled”。 在某些情况下,命令会引发的事件多于一个事件,或者命令会基于某种逻辑引发不同的事件吗?还是这始终是1-1的关系(1个命令将始终不引发任何事件,或者仅引发给定类型的单个事件)。 我之所以这样问是因为,如果这是事实,那么命令将始终引发同一事件,那么我可以基于该事实建立约定系统。我知道“ RaiseEvent”将导致“ EventRaised” ...

2
DDD CQRS-每个查询和每个命令的授权
摘要 是否应按命令/查询实现CQRS / DDD中的授权? 我是第一次使用或多或少严格使用DDD CQRS模式的在线应用程序。我遇到了一个问题,我无法真正解决这个问题。 我正在构建的应用程序是分类帐应用程序,允许人们创建分类帐,也允许其他人查看/编辑/删除分类帐,例如员工。分类帐的创建者应该能够编辑他创建的分类帐的访问权限。甚至可以更改所有权。该域具有两个聚合TLedger和TUser。 我读了很多有关DDD / CQRS关键字的文章,涉及安全性,授权等。大多数人说授权是通用子域,除非正在构建安全应用程序。 在这种情况下,核心域当然是对交易,余额和帐户感兴趣的会计域。但是还需要能够管理对分类帐的细粒度访问的功能。我想知道如何用DDD / CQRS术语进行设计。 在DDD教程中,所有命令都是通用语言的一部分。它们很有意义。它们是代表“真实事物”的具体动作。 因为所有这些命令和查询都是用户将在“现实生活”中执行的实际操作,所以授权的实现是否应与所有这些“命令”和“查询”结合使用?例如,用户将具有执行TLedger.addTransaction()的权限,但没有执行TLedger.removeTransaction()的权限。或者,将允许用户执行查询“ getSummaries()”,但不执行“ getTransactions()”。 三维映射将以用户分类帐命令或用户分类帐查询的形式存在,以确定访问权限。 或者,以分离的方式,将为用户注册名为“权限”的权限。然后将为特定命令映射权限。例如,权限“ ManageTransactions”将允许用户执行“ AddTransaction()”,“ RemoveTransaction()”等。 权限映射用户->分类帐->命令/查询 权限映射用户->分类帐->权限->命令/查询 那是问题的第一部分。或简而言之,CQRS / DDD中的授权应按命令还是按查询实施?还是应该将授权与命令分离? 其次,关于基于权限的授权。用户应该能够管理其分类帐或允许其管理的分类帐上的权限。 授权管理命令在分类帐中发生 我想到了将事件/命令/处理程序添加到Ledger聚合中,例如GrantPermission(),revokePermission()等。在这种情况下,强制执行这些规则将发生在命令处理程序中。但这将要求所有命令都包含发出该命令的用户的ID。然后,我将检查到TLedger中是否存在该用户执行该命令的权限。 例如 : class TLedger{ function addTransactionCmdHandler(cmd){ if (!this.permissions.exist(user, 'addTransaction') throw new Error('Not Authorized'); } } 用户中的授权管理命令 另一种方法是将权限包括到TUser中。TUser将具有一组权限。然后,在TLedger命令处理程序中,我将检索用户并检查他是否有权执行该命令。但这将要求我为每个TLedger命令获取TUser聚合。 class TAddTransactionCmdHandler(cmd) { …

2
CQRS是否设计过度?
我仍然记得存储库过去的美好时光。但是随着时间的流逝,存储库变得丑陋。然后,CQRS成为主流。他们很好,呼吸着新鲜空气。但是最近,我又一次又一次地问自己,为什么我不能在Controller的Action方法中保持正确的逻辑(尤其是在Web Api中,Action本身就是某种命令/查询处理程序)。 以前,我对此有一个明确的答案:我这样做是为了进行测试,因为很难使用所有这些不可模仿的单例和整体难看的ASP.NET基础结构来测试Controller。但是时代变了,如今,ASP.NET基础结构类对单元测试的友好程度更高(尤其是在ASP.NET Core中)。 这是一个典型的WebApi调用:添加了命令,并通知SignalR客户端有关此命令: public void AddClient(string clientName) { using (var dataContext = new DataContext()) { var client = new Client() { Name = clientName }; dataContext.Clients.Add(client); dataContext.SaveChanges(); GlobalHost.ConnectionManager.GetHubContext<ClientsHub>().ClientWasAdded(client); } } 我可以轻松地对其进行单元测试/模拟。而且,借助OWIN,我可以设置本地WebApi和SignalR服务器并进行集成测试(顺便说一下,速度非常快)。 最近,我越来越不愿意创建繁琐的命令/查询处理程序,并且我倾向于将代码保留在Web Api操作中。仅当重复逻辑或逻辑很复杂并且我想隔离它时,我才例外。但是我不确定我在这里做的事是否正确。 在典型的现代ASP.NET应用程序中管理逻辑的最合理方法是什么?什么时候将代码移动到Commands和Queries处理程序是合理的?有没有更好的模式? 更新。我发现这篇关于DDD-lite方法的文章。因此,看来我将代码的复杂部分移至命令/查询处理程序的方法可以称为CQRS-lite。

5
DDD,佐贺(Saga)和事件来源:可以将补偿行为简单地删除事件存储区吗?
我意识到上述问题可能会引起一些“什么?”,但让我尝试解释一下: 我试图将一些相关概念,基本上是Saga模式(http://www.rgoarchitects.com/Files/SOAPatterns/Saga.pdf)与事件源(DDD概念)结合起来:http : //en.wikipedia.org/wiki/Domain-driven_design) 一个很好的文章,将其包装在一起: https //blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-ii-of-ii/ 我一分钟就会解决这个问题,但是我想我必须先总结一下我对它的理解(这很可能是错误的,所以请纠正这种情况),因为这很可能会影响我为什么提出以下问题: Saga模式是一种代理,它给定操作(最终用户,自动化等,本质上是要更改数据的任何操作),将该操作划分为业务活动,并将这些活动中的每一个作为消息发送给Message Bus,依次将其发送到各个聚合根进行处理。 这些聚集的根可以完全自主地运行(关注点分离得很好,可扩展性强等)。 Saga实例本身不包含任何业务逻辑,该逻辑包含在向其发送活动的聚合根中。Saga中包含的唯一“逻辑”是“过程”逻辑(通常实现为状态机),它根据收到的动作(以及后续事件)确定要做什么(即:发送什么活动) Saga模式实现了一种分布式事务模式。即:当聚合根之一(可以再次自动工作,而又不了解彼此的存在)失败时,可能必须回滚整个操作。 这是通过让所有聚合根实现的,将它们的活动报告完成后返回给Saga。(无论成功还是失败) 如果所有聚合根均返回成功,则内部状态机是否由Saga决定下一步(或决定已完成) 在失败的情况下,Saga将参与最后一个动作的所有聚合根发出一个所谓的补偿动作,即:撤销每个聚合根所做的最后一个动作的动作。 如果操作是“加1票”,则可能只是在进行“减1票”,但可能会更复杂,例如将博客文章还原到以前的版本。 事件源(请参阅结合了这两个博客文章)旨在将保存在外部的每个汇总根源的每个活动的结果保存到集中式事件存储中(在这种情况下,这些更改称为“事件”) 此事件存储区是“事实的单一版本”,可用于简单地通过迭代存储的事件来重播所有实体的状态(本质上类似于事件日志) 将两者结合起来(即:让聚集的根使用事件源来将其更改外包,然后再将其报告回Saga)可以实现很多不错的可能性,其中之一是我的问题... 我觉得我需要把这件事从肩膀上移开,因为一口气要掌握很多。鉴于此上下文/心态(再次,请纠正错误) 问题:当汇总根接收到补偿动作并且如果该汇总根已使用事件源外包将其状态更改外包时,补偿动作不是在所有情况下都只是删除事件存储中针对该事件的最后一个事件给定总根?(假设持久实现允许删除) 这对我来说很有意义(这将是这种组合的另一个很大的好处),但是正如我所说的那样,我可能基于对这些概念的错误/不完全理解而做出这些假设。 我希望这不会太冗长。 谢谢。

2
如何在事件源中实施流程管理器
我正在研究一个小示例应用程序,以学习CQRS和事件源的概念。我有一个Basket汇总和一个Product应该独立工作的汇总。 这是一些伪代码来展示实现 Basket { BasketId; OrderLines; Address; } // basket events BasketCreated { BasketId; } ItemAdded { BasketId; ProductId; Quantity } AddItemSucceeded { BasketId; ProductId; Quantity } AddItemRevoked { BasketId; ProductId; Quantity } ItemRemoved { BasketId; ProductId; Quantity } CheckedOut { BasketId; Address } Product { ProductId; Name; Price; } …

2
SQL Server和Mongo可以一起使用吗?
我们有一个大型的面向新闻的网站,具有很高的网络流量。该体系结构是您经常看到的DB-回购层-服务层-Asp.Net MVC。我们一直看到的问题与读取性能有关。事实证明,从理论上讲,所有这些DDD域对象对于业务规则来说都是很棒的,但是在优化读取性能方面却变得更加艰辛。 作为一种解决方案,我正在考虑(对我们而言)全新的事物:使用noSQL。我想使用noSQL数据库存储在我们网站上显示的数据。我们无法摆脱我们的SQL Server(至少不会很快),但是在我看来,一个实际可行的步骤是将Mongo用作所有新开发的查询数据库。 我的问题是,它是否可以使用SQL Server作为您的记录和蒙戈作为查询数据库的数据库一起? 因此,当我们的一位编辑者更新记录时,数据将存储在SQL Server中。这是必要的,因为有太多的遗留代码无法在一夜之间重写。 但是,当网站上的查看者查看文章或文章列表时,我想利用Mongo与SQL Server的性能。为了使数据保持最新状态(例如15分钟或更短),SQL Server数据将需要刷新Mongo。RDBMS具有用于此类操作的复制工具,我想知道是否存在从SQL Server到Mongo的相同操作。Lync服务器,也许吗?
14 sql-server  nosql  cqrs  mongo 

3
从“快照”投影而不是事件存储中补水聚合
因此,尽管我从未有机会在实际项目中应用这些模式,但我一直在调动Event Sourcing和CQRS一段时间。 我了解将读取和写入问题分开的好处,并且感谢事件搜寻如何使将状态更改轻松地投影到与事件存储区不同的“读取模型”数据库中变得容易。 我不清楚的是,为什么您会从Event Store自身中重新添加聚合。 如果将更改投影到“读取”数据库很容易,为什么不总是将更改投影到其架构与您的域模型完全匹配的“写入”数据库呢?这实际上将是快照数据库。 我想这在野外的ES + CQRS应用程序中一定很常见。 在这种情况下,事件存储仅在由于架构更改而重建“写”数据库时有用吗?还是我想念更大的东西?

2
如何处理事件来源中的副作用?
假设我们要为金融应用程序实现一个小型安全子系统,该子系统将在检测到异常模式时通过电子邮件向用户发出警告。对于此示例,该模式将包括所描述的三个事务。安全子系统可以从队列中读取主系统中的事件。 我想得到的是一个警报,它是系统中发生的事件的直接结果,而没有中间模式来模拟模式的当前状态。 监控已激活 交易已处理 交易已处理 交易已处理 警报已触发(ID:123) 已发送警报电子邮件(ID:123) 交易已处理 考虑到这一点,我认为事件源可以在这里很好地应用,尽管我有一个没有明确答案的问题。在示例中触发的警报具有明显的副作用,需要发送电子邮件,这种情况只能发生一次。因此,在重放聚合的所有事件时不应发生这种情况。 在某种程度上,我看到需要发送的电子邮件类似于查询方生成的实现,这在CQRS / Event采购文献中已经见过很多次了,尽管差别不大。 在此文献中,查询端是由事件处理程序构建的,该事件处理程序可以在给定点生成状态的实现,从而再次读取所有事件。但是,在这种情况下,由于前面解释的原因,不能完全像那样完成。每个状态都是瞬态的想法在这里并不适用。我们需要记录警报发送到某个地方的事实。 对于我来说,一个简单的解决方案是使用其他表或结构,在其中保留先前触发的警报的记录。由于我们具有ID,因此我们可以检查之前是否发布了具有相同ID的警报。拥有此信息将使SendAlertCommand成为幂等。可以发出几个命令,但副作用只会发生一次。 即使考虑到该解决方案,我也不知道这是否暗示此体系结构对此问题有问题。 我的方法正确吗? 在哪里可以找到更多有关此的信息? 奇怪的是我还没有找到更多有关此的信息。也许我一直在使用错误的措词。 非常感谢!

1
我应该使用命令还是事件?
在总线通信中,命令和事件之间的区别在我看来似乎有点模糊。我知道命令应该只执行一次,而一个事件可以处理多次,但是我仍然不确定何时使用命令或事件。 让我们看一个例子: 当新用户注册到Web应用程序时,我们应该为他创建一个帐户并发送确认电子邮件。 创建帐户 -这似乎是将a发送CreateUserCommand到总线并由专门的组件处理的正确位置。 也许这甚至不应该使用异步总线通信来实现?我们希望用户能够立即登录到应用程序。对于总线,我们无法保证命令将在何时执行。 发送电子邮件 -组件创建帐户后,我可以看到2种可能性 向总线发送另一个命令 SendConfirmationEmailCommand 发布活动 UserAccountCreatedEvent 而不是让电子邮件发件人组件抓住它并完成任务。 一方面,我希望确认电子邮件仅发送一次(使用命令),另一方面,我相信可能有多个对新注册用户感兴趣的组件。记录器或SMS发送者。 您将如何实施?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.