|
Posted on
2012-01-31 00:06
netfocus
阅读( 3510)
评论()
编辑
收藏
举报
- CQRS is just a concept that we can seperate the application to two parts: the write part and the read part, that's all;
- Command: A command represents what user want to do, user can send a command to the system to ask the system what he or she wants to do; command doesn't have result. The client can make of the query data store when validating commands. On the server side, the only reason that such a command would fail would be due to concurrency. As such, validation can be performed on the client, checking that all fields required for that command are there, number and date ranges are OK, that kind of thing. The server would still validate all commands that arrive, not trusting clients to do the validation. What we see is that in this model, commands don’t need to be processed immediately – they can be queued. Another part is the issue of failed message processing due to the database being down or hitting a deadlock. There is no reason that such errors should be returned to the client – we can just rollback and try again. When an administrator brings the database back up, all the message waiting in the queue will then be processed successfully and our users receive confirmation. The system as a whole is quite a bit more robust to any error conditions. Thinking through the order in which commands and events are processed can lead to notification patterns which make returning errors unnecessary. Messaging guarantees that the command will get it's chance to be processed even if the database is deadlocked or even the entire server is down.
- Command Handlers:The handlers of the command, one command only has one command handler; we’re not returning errors to the client (who is already sending us valid commands), maybe all we need to do on the client when sending a command is to tell the user “thank you, you will receive confirmation via email shortly”. We don’t even need the UI widget showing pending commands. Command handlers are responsible for:Performing any required validation on the command.Invoking the domain — coordinating objects in the domain, and invoking the appropriate behaviour on them.Command handlers are application services, and each execution represents a separate unit of work (e.g. a database transaction). There is only one command handler per command, because commands can only be handled once — they are not broadcast out to all interested listeners like event handlers.
- Command Service:Dispatch the command to a corresponding command handler.
- Domain: The DDD domain model, seperated by the aggregates, represents the domain structure and business logic; There is nothing that states that all commands must be processed by the same domain model. Arguably, you could have some commands be processed by transaction script, others using table module (AKA active record), as well as those using the domain model. Event-sourcing is another possible implementation. Another thing to understand about the domain model is that it now isn’t used to serve queries. So the question is, why do you need to have so many relationships between entities in your domain model? Do we really need a collection of orders on the customer entity? In what command would we need to navigate that collection? In fact, what kind of command would need any one-to-many relationship? And if that’s the case for one-to-many, many-to-many would definitely be out as well. I mean, most commands only contain one or two IDs in them anyway. Any aggregate operations that may have been calculated by looping over child entities could be pre-calculated and stored as properties on the parent entity. Following this process across all the entities in our domain would result in isolated entities needing nothing more than a couple of properties for the IDs of their related entities – “children” holding the parent ID, like in databases. In this form, commands could be entirely processed by a single entity – viola, an aggregate root that is a consistency boundary.
- Aggregate: A cluster of associated objects that are treated as a unit for the purpose of data changes. External references are restricted to one member of the Aggregate, designated as the root. A set of consistency rules applies within the Aggregate's boundaries. Problem: It is difficult to guarantee the consistency of changes to objects in a model with complex associations. Invariants need to be maintained that apply to closely related groups of objects, not just discrete objects. Yet cautious locking schemes cause multiple users to interfere pointlessly with each other and make a system unusable. [DDD, p. 126] Solution: Cluster the Entities and Value Objects into Aggregates and define boundaries around each. Choose one Entity to be the root of each Aggregate, and control all access to the objects inside the boundary through the root. Allow external objects to hold references to root only. Transient references to the internal members can be passed out for use within a single operation only. Because the root controls access, it cannot be blindsided by changes to the internals. This arrangemens makes it practical to enforce all invariants for objects in the Aggregate and for the Aggregate as a whole in any state change. The whole point of an aggregate and its aggregate root is to define an aggregate boundary. This boundary states that inside things should be consistent hence invariants should be satisfied. But it also means that outside, you cannot enforce invariants. If you need to enforce invariants outside an aggregate, it means, it's not a proper aggregate.
- Domain Event:Represents what happened of the aggregates;
- Event Sourcing:Domain events can be used to rebuild the aggregate by replaying them of all;
- Event Bus:A bus to publish domain event, there are synchronous event bus, asynchronous event bus, and composite event bus;
- Repository:Act as an in-memory collection which contains all the aggregates;
- Event Store: Stores all the persisted domain events;
- Is suited to complex domains, commonly applied along with DDD;
- Separate the write and read model;
- Use message to synchronize the write and read model, domain event is a type of message;
- Task-based UI design; A core element of CQRS is rethinking the design of the user interface to enable us to capture our users’ intent; A task based UI is quite different from a CRUD based UI. In a task based UI you track what the user is doing and you push forward commands representing the intent of the user. I would like to state once and for all that CQRS does not require a task based UI. We could apply CQRS to a CRUD based interface (though things like creating separated data models would be much harder). There is however one thing that does really require a task based UI… That is Domain Driven Design. The Application Service Layer in Domain Driven Design represents the tasks the system can perform. It does not just copy data to domain objects and save them… It should be dealing with behaviors on the objects… Before going further let’s look at what happened if we did; there would be no verbs in our ubiquitous language except for “Create”, “Delete”, and “Change”. While there exist many domains where this is what the Ubiquitous Language is actually like, you probably should not be using Domain Driven Design for such systems.
- One command effect one aggregate;
- Aggregate defines the transactional consistency boundary; Inside the aggregate, consistency is guaranteed. Outside... it's not. So generally you should not have operations that spans several aggregates and have to be consistent. If you need a transaction that spans two aggregates, you should first review your aggregate boundaries.
- Should know the distinction between: message, command, event;
- Events are not about "field x changed", but about some change happening in real world with it's context and meaning, expressed in the language that makes sense to involved parties.
- Event Sourcing should be used; Event sourcing capture all changes to an application state as a sequence of events. We can query an application's state to find out the current state of the world, and this answers many questions. However there are times when we don't just want to see where we are, we also want to know how we got there. Event Sourcing ensures that all changes to application state are stored as a sequence of events. Not just can we query these events, we can also use the event log to reconstruct past states, and as a foundation to automatically adjust the state to cope with retroactive changes.
- Use the eventual consistency to increase your saclability and availability, not transactional consistency;
- We always use unit of work to tracking and store all the happened domain events;
- There is no assurance of order between event handlers;
- The inter-aggregate communication is a business process, and the best way to implement the business process (specially the long-running process transactions) is saga;
- Aggregates receive commands and publish events, and sagas receive events and dispatch commands;
- If there is a situation where more than a single AR was involved, that indicates that there was some sort of business process (as compared to business logic) will occur.
- How to persist the saga status? Also use the event sourcing, called state-change-sourced saga with a full history of state changes;
- Rules to design aggregates: 1) model true invariants; 2)design small aggregates; 3) reference other aggregates by id; 4) Use eventual consistency between aggregates(after asking whose job it is);
- In CQRS, the command handler is doing the same thing as the application service in Eric's DDD;
- Command does not have return value, and we must ensure the command is valid before it is sent to server; When the command is processing asynchronously, we might show user information like "Your request is being processing, please wait for a few seconds.". As soon as the command is succeed (or failed), user is automatically taken to the next screen. This is trivial to do with AJAX in web UI. Desktop UIs are even simpler.
- In some cases, use SendAndBlock pattern to communicate between web client and server; One thing to notice in the SendAndBlock is that the web page renders synchronously from the user's perspective, the interaction is synchronous and blocking, even though behind the scenes NServiceBus is doing asynchronous messaging.
- There are two main integration points in Ncqrs-NServiceBus tandem. First one is transporting commands and the second one is publishing events;
- There is not need the domain service(builder block in Eric's DDD) again, replaced by event and saga;
- You should never delete an aggregate. You should mark it is out of date or unavailable. Deciding what to do with the remaining references is a business decision;
- There are two kinds of logic: 1) Processing domain logic; 2) Processing selection logic; Processing domain logic is the business logic that manipulates the application. Processing selection logic is the logic that chooses which chunk of processing domain logic should run depending on the incoming event. You can combine these together, essentially this is the Transaction Scriptapproach, but you can also separate them by putting the processing selection logic in the event processing system, and it calls a method in the domain model that contains the processing domain logic;
- About the validation in CQRS: 1) do validation at the client before the command is send to server; 2) In the command handler, also do the validation as the client does; 3) In the command handler, do the process logic validation, such as the uniqueness check; 4) Put the business logic validation inside the aggregate; 5) In some cases, you could encounter a concurrency violation that cannot be detected until the aggregate root is touched. We just throw exception when this occur and there won't generate dirty data as we do all the data changes in the unit of work and all updates are in a transaction, if any exception occur the transaction will be rollback.
- Current State Backing: Pros : 1) Easier to retrofit on a legacy project; 2)Well known technology/tools/skill sets; 3)Easier sell for an organization. Cons: 1) Dual models cost more and contain risk; 2) Non-Event Centric. Event Storage Backing: Pros: 1) Single Event Centric Model; 2) Simplified/Better Testing; 3) Conflict Management. Cons: 1) Harder to sell to an organization; 2) Less known tools/technologies (though you can implement the Event Store in a RDBMS which kind of mitigates this); 3) Very difficult to migrate a legacy app to.
- In most systems, the number of reads (queries) is typically an order of magnitude higher than than the number of writes (commands) — particularly on the web. It is therefore useful to be able to scale each side independently.
- When we say "CQRS" architecture, this often means: "CQRS Principle" + "Message-driven architecture" + "Domain-Driven Design" + "Event Sourcing", we also called this DDDD(Distributed Domain-Driven Design) .
- What is a saga in contrast to a process manager? The intent of these patterns is different. A process manager is a workflow pattern which can, as you say, be built on top of a state machine. A process manager will retain state between messages, and will contain logic in order to determine which action should be taken in response to a message (for example, transitioning state or sending another message). Some frameworks incorrectly refer to these as sagas.By contrast, a saga (according to the original definitions) is a pattern intended to help manage failures. It involves multiple workflows across systems, where each will allow some form of compensating action to be taken in a later transaction in the case of a failure elsewhere.This compensation is the defining characteristic of a saga. Note that the saga itself does't know what the compensating action might be. Sagas are often implemented using the routing slip pattern. A system participating in a saga might use a process manager to actually handle its piece of processing.
|