你的位置:首页 > 软件开发 > ASP.net > CQRS FAQ (翻译)

CQRS FAQ (翻译)

发布时间:2016-06-08 21:00:07
我从接触ddd到学习cqrs有6年多了, 其中也遇到了不少疑问, 也向很多的前辈牛人请教得到了很多宝贵的意见和建议. 偶尔的机会看到国外有个站点专门罗列了ddd, cqrs和事件溯源的常见问题. 其中很多也是我一路过来都曾遇到过的. 这是原站地址http://www.cqrs.n ...

  我从接触ddd到学习cqrs有6年多了, 其中也遇到了不少疑问, 也向很多的前辈牛人请教得到了很多宝贵的意见和建议. 偶尔的机会看到国外有个站点专门罗列了ddd, cqrs和事件溯源的常见问题. 其中很多也是我一路过来都曾遇到过的. 这是原站地址http://www.cqrs.nu/Faq. 在ENODE群中不少新学习cqrs的朋友都会遇到一些类似的入门问题, 作为群管理员的我也想为群里朋友做点贡献, 所以有了翻译一下CQRS FAQ的念头, 并加入一些自己的理解, 希望对大家会有所帮助. 

PS. 本人英语能力也一般,所以难免有些翻译拗口的地方, 很多地方可能只能意会不可言传. :)

Domain-Driven Design 领域驱动设计

What is the blue book that everyone is talking about? 那本大家都在讨论的蓝皮书是什么呢?

This one? It is the defining text on Domain-Driven Design, by Eric Evans, the founder of DDD. It comes highly recommended.

这个? 他是DDD的创始人Eric Evans 对于领域驱动设计书面上的定义. 真的是强烈推荐.

What is a ubiquitous language? 什么是统一语言?

A set of terms used by all people involved in the domain, domain model, implementation, and backends. The idea is to avoid translation, because as Eric Evans points out,

Translation blunts communication and makes knowledge crunching anemic.

That is, every time we have to translate concepts between people — "oh, you're using 'user' in these cases where I'm using 'account'" — we lose a direct ability to think clearly about the thing we are building and to let new knowledge flow back and forth between domain and implementation.

Investing in a ubiquitous language pays off in that it makes communication clearer, and allows teams to see more opportunities.

被所有涉及到领域,领域模型,开发实现和后端操作人员所使用的一个术语集合. 这个主意是为了避免语义转化, 因为Eric Evans提出 "语义的转化使得交流变得迟钝生硬而且使得知识消化起来很乏力. "

那好比每次我们都不得不在大家沟通时进行概念转化 -- "噢, 你正在那些场景中用'user'而我用的是'account' -- 我们丢失了一个能把我们正在构建的事物想清楚的直接机会, 而且无谓的在领域和实现之间产生了新的知识点.

把精力投在统一语言上是值得的因为它使得交流更加清晰并且能让团队获得更多的先机.

What is a bounded context? 什么是边界上下文

A division of a larger system that has its own ubiquitous language and domain model. The pricing, shipping, and recommendations aspects of an online retailer would count as separate bounded contexts, as they have significantly different concerns.

拥有自己统一语言和领域模型的一种大系统的划分结果. 在线零售系统的定价, 运输和推荐这些方面会被看成分别的边界上下文, 因为他们有各自的明显不同的关注点.

As with other DDD concepts, bounded contexts are most valuable when carried through into the implementation.

和其他的DDD概念一起, 边界上下文是我们进行开发实现中最优价值的.

How do I go about identifying bounded contexts? 我该如何着手识别边界上下文?

Some common things to look for are:

  • The natural boundaries in an organization (within a bounded context, most often you'll find that people collaborate and communicate closely; between bounded contexts the communication is less, and often asynchronous)
  • Where the same word is given different meanings (product to pricing is a thing with a price; product to shipping is a thing with a weight and dimensions, etc.)

去找一些通用的事物:

  组织结构的自然边界(你经常会发现在一个边界上下文中人们都是紧密地进行协作和交流; 而在边界上下文之间的交流是比较少的而且不是那么实时性的)

  不同的边界上下文中同一个单词往往表达了不同的含义(产品对于销售价格来说是一个有价格的物体; 产品对于运输来说是一个有重量和大小的物体).

Generally, good bounded contexts look like products (a pricing strategy product, a shipping calculation product, a product recommendation engine product, etc.) This aligns well with the products-over-projects team structure.

 一般来说, 好的边界上下文看上去就像不同范畴的商品(一个带有售价策略的商品, 一个可以运输计费的商品, 一个推荐商品引擎中的商品 , 等等). 这很符合不同项目团队结构里的不同商品感念的情况.

How isolated should a bounded context be from the rest of my system? 如何从系统中分离出边界上下文呢?

Quite strongly. In general, direct dependencies are best avoided. For example, in .Net separate assemblies would be fairly sensible. In a distributed paradigm, such as SOA or microservices, then finding process boundaries between bounded contexts would be normal.

非常重要的是一般来说最好是避免直接的依赖. 像.Net 开发中分成多个程序集是很明智的做法. 在一个分布式系统范例中, 像面向服务架构和微服务, 在边界上下文中去发现业务处理的边界是比较常见的做法.

How can I communicate between bounded contexts? 边界上下文之间如何交互呢?

Exclusively in terms of their public API. This could involve subscribing to events coming from another bounded context. Or one bounded context could act like a regular client of another, sending commands and queries.

仅仅通过他们的公开API.  这可能涉及到订阅另一个边界上下文的事件. 或者某个边界上下文可以像另一个边界上下文的普通客户端一样发送命令或查询.

What are entities? What are value objects? 什么是实体? 什么是值对象?

Entities or reference types are characterized by having an identity that's not tied to their attribute values. All attributes in an entity can change and it's still "the same" entity. Conversely, two entities might be equivalent in all their attributes, but will still be distinct.

实体或者引用类型被描述成拥有一个不和他们属性值相关联的标识. 一个实体内的所有属性都可以变化而且变化之后它还是原来的那个实体.  相反地, 两个不同的实体可能他们所有的属性值都一样, 但他们仍然是不同的实体.

Value objects have no separate identity; they are defined solely by their attribute values. Though we are typically talking of objects when referring to value types, native types are actually a good example of value types. It is common to make value types immutable. For example,String in many languages is immutable, and every time you want to "change" a get='_blank'>string, you derive a new one.

值对象没有各自的标识; 他们仅仅通过他们的属性值来定义. 尽管我们经常讨论引用值类型的对象, 但是本地化的类型确实是值类型的一个很好的列子. 它往往使得值类型是不可变的. 比如, 在许多语言中, 字符串都是不可变的, 每次你想改变一个字符串的时候, 你实际上是派生了一个新实例. 

From an event sourcing perspective, both entities and value objects play important roles in the domain, but only entities need be persisted, since only these change.

从事件溯源的角度看的话, 在领域中实体和值对象都扮演了十分重要的角色, 但是只有实体是要被持久化的, 因为只有他们是会变化的. (其实值对象一般都包含在实体内一同被持久化了)

Commands and events 命令和事件

What is an event? 什么是事件?

An event represents something that took place in the domain. They are always named with a past-participle verb, such as OrderConfirmed. It's not unusual, but not required, for an event to name an aggregate or entity that it relates to; let the domain language be your guide.

一个事件表达了领域里发生了什么. 他们总是被命名为过去时, 比如OrderConfirmed(订单被确认了). 为一个事件命名成一个和它相关的聚合根或者实体的名字并不是不寻常的, 但不是必须的. 让领域语言作为你的指导.

Since an event represents something in the past, it can be considered a statement of fact and used to take decisions in other parts of the system.

既然一个事件表达了过去发生的事情, 他可以被认为是一个事实的阐述并且可以被系统的其他部分用来做决定的依据.

What is a command? 什么是命令?

People request changes to the domain by sending commands. They are named with a verb in the imperative mood plus and may include the aggregate type, for example ConfirmOrder. Unlike an event, a command is not a statement of fact; it's only a request, and thus may be refused. (A typical way to convey refusal is to throw an exception).

人民通过发送命令来要求领域发生变化. 他们被命名成一个带有启示语气的一个动词而且可以带有聚合根类型, 比如ConfirmOrder(确认订单). 不像一个事件, 一个命令并不是一个事实的阐述, 他只是一个请求, 而且可能被拒绝执行. (系统要表达拒绝的典型方式是抛出一个异常)

What does a command or an event look like? 命令和事件看上去是什么样的?

Commands and events are simply data structures that contain data for reading, and no behavior. We call such structures "Data Transfer Objects" (DTOs). The name indicates the purpose. In many languages they are represented as classes, but they are not true classes in the real OO sense.

命令和事件是简单的数据结构, 他们包含一些读取的数据且没有行为. 我们将这样的结构称为数据传输对象(DTO). 他们的名字表达了他们的意图. 在许多语言中, 他们以类的形式呈现, 但是他们往往不是真正意义上面向对象概念的类.

Here's an example of a command: 

命令的例子:

public class ConfirmOrder {  public Guid OrderId;}

And here's an example of an event:

事件的例子:

public class OrderConfirmed {  public Guid   OrderId;  public DateTime ConfirmationDate;}

What is the difference between a command and an event? 命令和事件的不同点是什么?

Their intent.

主要体现在他们不同的意图上. 

What is immutability? Why are commands and events immutable? 什么是不变性? 为什么命令和事件是不可变的?

For the purpose of this question, immutability is not having any setters, or other methods which change internal state. The string type in Java and C# is a familiar example; you never actually change an existing string value, you just create new string values based on old ones.

Commands are immutable because their expected usage is to be sent directly to the domain model side for processing. They do not need to change during their projected lifetime in traveling from client to server.

Events are immutable because they represent domain actions that took place in the past. Unless you're Marty McFly, you can't change the past, and sometimes not even then.

就这个问题的而言, 不变性就是没有任何设置方法也没有任何可以改变内部状态的方法. java和c#中的字符串类型就是个类似的例子. 你永远不能去改变一个已存在的字符串, 你只是基于老的值创建了一个新的字符串.

命令的不可变是因为他们预期的使用方式是直接被送达到领域去执行.他们不需要在客户端至服务端的传输期间做任何的改变.

事件的不可变是因为他们表现了领域在过去发生的动作.  除非你是Marty McFly(回到未来的主角), 你不能改变过去, 有时甚至都没有过去.

What is command upgrading?什么是命令升级?

Upgrading commands becomes necessary when new requirements cause existing commands not to be sufficient. Maybe a new field needs to be added, for example, or maybe an existing field should really have been split into several different ones.

当新的需求导致现有命令不够充分时, 升级命令就变得很有必要了. 有可能是需要加一个新的字段, 也有可能一个已存在的字段应该被分解成若干个不同的字段.

How do I upgrade my commands? 应该如何升级命令?

How you do the upgrade depends how much control you have over your clients. If you can deploy your client updates and server updates together, just change things in both and deploy the updates. Job done. If not, it's usually best to have the updated command be a new type and have the command handler accept both for a while.

你如何升级命令取决于你对客户端的掌控度. 如果你可以一起发布你的客户端和服务端, 那就可以同时在两端进行修改并发布升级后的版本, 那升级就搞定了. 如果不是的话, 通常最好的做法是用一个新的命令来代替要升级的命令, 并且同时让命令处理器能够接受他们.

Could you give an example of names of some versioned commands? 能否给出一个带有版本信息的命令命名的一个例子?

Sure.  当然.

UploadFileUploadFile_v2UploadFile_v3

It's just a convention, but a sane one.

这就是一种约定, 但是却是健全的做法.

Command/Query Responsibility Segregation 命令/查询职责分离

What is CQRS? 什么是CQRS?

CQRS means "Command-query responsibility segregation". We segregate the responsibility between commands (write requests) and queries(read requests). The write requests and the read requests are handled by different objects.

CQRS的意思是"命令查询职责分离". 我们将命令(写请求) 和查询(读请求)的职责分离开. 写请求和读请求由不同的对象进行处理.

That's it. We can further split up the data storage, having separate read and write stores. Once that happens, there may be many read stores, optimized for handling different types of queries or spanning many bounded contexts. Though separate read/write stores are often discussed in relation with CQRS, this is not CQRS itself. CQRS is just the first split of commands and queries.

就是这样. 我们可以进一步以分离读写库来进行数据存储的分离. 一旦这么做了, 那么就会有很多的读存储, 他们可以被优化以至于可以处理不同类型的查询或者这些读存储可以跨越多个边界上下文.

CQRS sounds like one of those newfangled diets. Who made up the term? CQRS听上去像个新流行的东西. 谁想出的这个术语?

Greg Young.

He has been complaining for years about search engines innocently asking "Did you mean CARS?" when one searches for CQRS.

Greg young. 他已经对搜索引擎天真地把CQRS当成CARS抱怨了好几年了.

I've heard there's something called CQS too. What is it, and how does it relate to CQRS? 我也已经有听到过CQS这种东西. 它是什么? 它和CQRS有什么关系?

CQS means "Command-query separation". It was introduced by Bertrand Meyer as part of his work on the Eiffel programming language.

CQS的 意思是"命令查询分离". 它是由Bertrand Meyer以他在Eiffel 编程语言工作上的一部分来介绍的.

It means that a method is either a command performing an action, or a query that returns data, but not both. Being purely action-performing methods, commands always have a void return type. Queries, on the other hand, should be idempotent, that is, they don't have any visible side effects on the system.

他的意思是一个方法要么是一个执行动作的命令, 要么是一个带有返回数据的查询, 但不是同时拥有这两种行为. 作为纯粹的动作执行方法, 命令执行方法总是没有返回值的. 而另一面,查询应该是幂等的, 就是说他们不会对系统造成任何可见的副作用. 

(在Eric Evans的ddd原著里, 柔性设计章节中也有提到, 领域的执行应该分为业务逻辑的计算和改变领域对象状态, 业务计算执行多少次都是没有副作用的,因为计算方法的输入和输出参数都是值对象, 而将计算结果赋值到领域对象上则是真正改变领域状态的时候. 这样使得程序在编写时职责更加明确, 这里我只是拿出来和cqs中提到的无副作用进行一个类比, 让大家体会一下无副作用的含义.其实cqrs的领域处理也正是ddd中提到的这种做法, 聚合根的公共方法里只是进行业务计算产生领域是事件, 而聚合根的eventhandler中才是真正改变聚合根状态的地方.  这里可能扯得有点远了:) )

Originally, CQRS was called "CQS", too. But it was determined that the two are different enough for CQRS to have its own name. The main distinguishing feature is this:

一开始, CQRS也曾被称作CQS. 但是这两者也有足够的不同之处以至于CQRS拥有他自己的名字. 主要的特征区别是:

  • CQS puts commands and queries in different methods within a type.
  • CQRS puts commands and queries on different objects.
  • CQS将命令和查询作为不同的方法放在一个类中.
  • CQRS将命令和查询放到不同的对象上.

Can CQRS ever be a simplification? CQRS能成为一个简单的事物吗?

Sure. Generic repositories are a common sight in many systems. They work well in CRUD scenarios - typically, those you may not be applying DDD to. They tend to work out fine for creates, updates, deletes, and reading individual entities. But as soon as there's a query that spans multiple entities where should it go?

当然可以. 一般的仓储在很多系统中已经司空见惯了. 他们已经在CRUD这种典型场景中工作得很不错了, 而那些场景中你可能并没有运用DDD. 他们想更好地解决独立实体的增删改查. 但是一旦出现了一个跨域多个实体的查询后, 它应该何去何从? (因为一般来说一个仓储只能对一种实体进行CRUD操作)

Rather than agonizing over it, and trying to shoe-horn queries into the generic repository arrangement, it's far easier to put them on a separate object. No questions where they go, and they can return simple, lightweight DTOs of data.

CQRS doesn't have to mean doing event sourcing, introducing commands, event, read sides, sagas, and so forth.

比起为此烦恼以及试图将这种查询融入到一般的仓储里,将查询放到不同的对象上就简单得多了.不管他们怎么查都不是问题, 他们可以返回简单的, 轻量级的数据传输对象. CQRS并不是说一定要用事件溯源, 命令, 事件, 读端, sagas(流程处理器)之类的高大上东西. 

Will CQRS not make my application more complex? CQRS不会使我的应用程序变得更复杂吧?

A typical CQRS + Event Sourcing system will seemingly have more components, since commands, events, exceptions, and queries become part of the public interface. Aggregates, command handlers, read side projections, sagas, and clients further contribute to the proliferation of components.

一个典型的CQRS+ Event Sourcing系统看上去会有更多的组件, 因为命令,事件, 异常和查询都成为了公开接口的一部分. 聚合根, 命令处理器, 读库投影, 流程处理器以及客户端进一步使得组件更为扩大化了.

However, each component is neatly uncoupled from the rest. Originally, "complex" means "braided together". The components in a CQRS+ES system are independent in a way that favors reasoning about the system, and responding to changing requirements:

然而每个组件都是巧妙得和其他部分解耦了. 本来, "复杂" 的意思就是"编制在一起".  在CQRS+ES系统中的组件都是以一种能更好地推理出系统以及应对需求变化的方式独立存在的.

  • The public interface of message types forms a layer of your application that encourages you to think in terms of user intent, not updating data.

  • The division of the system into client, write side, and read side makes it easy to divide work between various teams.

  • Perhaps most importantly, testing becomes very natural, even of the most important and complex parts of the business logic.

 

  • 消息类型的公开接口组成了应用的一个层次, 鼓励你以符合用户意图的方式去思考, 而不只是更新数据.
  • 将系统分解成客户端 , 写端和读端使得在不同团队之间分配工作变得更简单了.
  • 可能最重要的是 测试变得非常自然, 即使是最重要以及最复杂的业务逻辑部分也是如此.

Should the write side always be independent of the read side? 写端是不是应该总是不依赖读端?

No. But it often helps - for example, by enabling event sourcing to be used on the write side, which can offer a lot of benefits.

答案是否定的. 但是如果不依赖往往有很多的帮助. 比如, 在写端使用了事件溯源, 那么可以带来很多好处.

原标题:CQRS FAQ (翻译)

关键词:

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

可能感兴趣文章

我的浏览记录