CQRS-简单的架构设计 已翻译 100%

不死的达芬奇 投递于 2018/08/03 14:19 (共 17 段, 翻译完成于 08-08)
阅读 16530
收藏 38
3
加载中

SOME SAY: “CQRS IS HARD!”
IS IT? WELL, I USED TO THINK THE SAME! BUT WHEN I STARTED WRITING MY FIRST PIECE OF SOFTWARE USING CQRS, IT QUICKLY TURNED OUT THAT IT IS RATHER UNCOMPLICATED. WHAT IS MORE, I THINK THAT IN LONG TERM IT IS MUCH EASIER TO MAINTAIN SOFTWARE WRITTEN IN THIS WAY.

I have started to think, what is the reason that people see it as hard and complex at the beginning? I have a theory: it has rules! Entering the world with rules is always uncomfortable, we need to adjust to rules. In this post I’d like to prove that in this case those rules are quite digestible.

已有 1 人翻译此段
我来翻译

ON THE WAY TO CQRS…

Basically, we can say that CQRS is an implementation of Command Query Separation principle to the architecture of software. What I noticed during my work in this approach is that there is a couple of steps between simplest CQS implementation and real full-blown CQRS. I think that those steps smoothly introduce rules I have mentioned before.

While the first steps are not fulfilling my definition of CQRS (but are sometimes so called), they can still introduce some real value to your software. Each step introduces some interesting ideas that may help to structure or clean your codebase/architecture.

Usually, our journey starts with something that looks like this:

已有 1 人翻译此段
我来翻译

This is typical N-Layer architecture as all of us probably know. If we want to add some CQS here, we can “simply” separate business logic into Commands and Queries:

If you are working with legacy codebase this is probably the hardest step as separating side-effects from reads in spaghetti code is not easy. In the same time this step is probably the most beneficial one; it gives you an overview where your side effects are performed.

Hold for a second! You are talking about CQS, CQRS, but you have not defined what Command or Query is!

That is true. Let’s define them now! I’ll give you my personal, intuitive definition of Command and Query here. It is not exhaustive and definitely should be deepened before implementation.

已有 1 人翻译此段
我来翻译

Command – First of all,firing command is the only way to change the state of our system. Commands are responsible for introducing all changes to the system. If there was no command, the state of the system remains unchanged! Command should not return any value. I implement it as a pair of classes: Command and CommandHandler. Command is just a plain object that is used by CommandHandler as input value (parameters) for some operation it represents. In my vision command is simply invoking particular operations in Domain Model (not necessarily one operation per command).

Query – Analogically, query is a READ operation. It reads the state of the system, filters, aggregates and transforms data to deliver it in the most useful format. It can be executed multiple times and will not affect the state of the system. I used to implement them as one class with some Execute (…) method, but now I think that separation to Query and QueryHandler/QueryExecutor may be useful.

已有 1 人翻译此段
我来翻译

Going back to the diagram, I need to clarify one more thing; I’ve smuggled here one additional change, Model became the Domain Model. By the Model I understand a group of containers for data while Domain Model encapsulates essential complexity of business rules. This change is not directly affecting our further considerations as we are interested in architectural stuff here. But it is worth mentioning that despite the fact that the command is responsible for changing state of our system, the essential complexity should be placed in the Domain Model.

OK, now we can add new command or write new query. After short period of time it will be quite obvious that Domain Model that works well for writing is not necessarily perfect for reading. It is not a huge discovery that it would be easier to read data from some specialized model:

已有 1 人翻译此段
我来翻译

We can introduce separated model, mapped by ORM and use it to build our queries, but in some scenarios, especially when ORM introduces overhead, it would be useful to simplify this architecture:

I think that this particular change should be well thought out!

Now the problem is that we still have READ and WRITE model separated only at the logical level as both of them shares common database. That means we have separated READ model but most likely it is virtualized by some DB Views, in better case Materialized Views. This solution is OK if our system is not suffering from performance issues and we remember to update our queries along with WRITE model changes.

已有 1 人翻译此段
我来翻译

The next step is to introduce fully separated data models:


From my point of view this is the first model that fulfills original idea presented by Greg Young, now we can call it CQRS. But there is also a catch! I’ll write about it later.

已有 1 人翻译此段
我来翻译

CQRS != EVENT SOURCING

Event Sourcing is an idea that was presented along with CQRS, and is often identified as a part of CQRS. The idea of ES is simple: our domain is producing events that represent every change made in system. If we take every event from the beginning of the system and replay them on initial state, we will get to the current state of the system. It works similarly to transactions on our bank accounts; we can start with empty account, replay every single transaction and (hopefully) get the current balance. So, if we have stored all events, we can always get the current state of the system.

While ES is a great method to store the state of the system is not necessarily required in CQRS. For CQRS, it is not important how Domain Model is actually stored and this is just one of options.

已有 1 人翻译此段
我来翻译

READ AND WRITE MODEL

The idea of separated models seems to be quite clear and straight-forward when we are reading about CQRS but it seems to be unclear during implementation. What is the responsibility of WRITE model? Should I put all data into my READ model? Well, it depends!

WRITE MODEL

I like to think about my WRITE model as about the heart of the system. This is my domain model, it makes business decisions, it is important. The fact that it makes business decisions is crucial here, because it defines major responsibility of this model: it represents the true state of the system, the state that can be used to make valuable decisions. This model is the only source of truth.

If you want to know more about designing domain models I recommend reading about Domain Driven Design technique philosophy.

已有 1 人翻译此段
我来翻译

READ MODEL

In my first attempt to CQRS I have used WRITE model to build queries and … it was OK (or at least worked). After some time we reach the point in the project where some of our queries were taking a lot of time. Why? Because we are programmers and optimization is our second nature. We designed our model to be normalized, so our READ side were suffering from JOINs. We were forced to pre calculate some data for reports to keep it fast. That was an quite interesting, because in fact we have introduced a cache. And from my point of view this is the best definition of READ model: it is a legitimate cache. Cache that is there by design, and is not introduced because we have to release project and non-functional requirements are not met.

The label READ model can suggest that it is stored in a single database and that’s it. In fact READ model can be very complex, you can use graph database to store social connections and RDBMS to store financial data. This is the place where polyglot persistence is natural.

已有 1 人翻译此段
我来翻译
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(4)

SimonAt
SimonAt
看上去好麻烦, 和微服务比有啥优势??,
ahyyxx222
ahyyxx222
给个直接的落地方案:1.增删改业务系统和查询系统分别布署。2.两个系统共用库表。3业务系统用的业务实体只含业务必须字段,查询系统用的实体是在业务实体的基础上增加了更多冗余字段,然后映射到和业务实体同一张表。4.命令引发的增删改service操作只会操作业务字段变更,查询系统收到异步消息或定时扫描去按各种查询场景需要,补足冗余字段甚至建立新的查询实体。好处:业务系统不再考虑冗余字段的维护,最小化操作,简化维护和扩展的业务复杂度。查询需求对业务代码无侵入性,不必在创建或修改逻辑同时附带多余操作,异步补全查询所需的字段和实体的值,有点像在维护物化视图。通来查询实体来跨模块或跨微服务,保护了业务实体的有效边界。麻烦:多维护一套service用于处理查询需求。异步会导致的轻微数据不同步,不过因为是同库同表,所以只是部分字段不同步,可以自行用各种手段弥补。
xdev
xdev
看来很多CQRS,从来没有看到过物理W/R分离时的数据一致性好方案,ES也没有看到具体的干活,谁tms说CORS+ES+RW分离实现容易,我保证不打死你
h4cd
h4cd
每一篇讲 CQRS 的文章都会先讲清楚 WHAT is Command and WHAT is Query?然后总是会想起大学学习 Windows MVC 编程的时候那些 command 和 call 之类的控件的区别。。。
返回顶部
顶部