CQRS

What is CQRS數據庫

 

CQRS means Command Query Responsibility Segregation. Many people think that CQRS is an entire architecture, but they are wrong. CQRS is just a small pattern. This pattern was first introduced by Greg Young and Udi Dahan. They took inspiration from a pattern called Command Query Separation which was defined by Bertrand Meyer in his book 「Object Oriented Software Construction」. The main idea behind CQS is: 「A method should either change state of an object, or return a result, but not both. In other words, asking the question should not change the answer. More formally, methods should return a value only if they are referentially transparent and hence possess no side effects.」 (Wikipedia) Because of this we can divide a methods into two sets:設計模式

  • Commands - change the state of an object or entire system (sometimes called as modifiers or mutators).
  • Queries - return results and do not change the state of an object.

CQRS意味着命令與查詢的責任分離。 不少人認爲CQRS是總體架構,但他們錯了。CQRS只是一個設計模式。 這種模式是首先由Greg Young和Udi Dahan提出。他們的靈感來源於Bertrand Meyer的書"Object Oriented Software Construction"中所定義命令查詢分離的模式。在CQS的主要理念就是是:"方法應該能夠更改對象的狀態或者返回結果,但不能同時兼具。換句話說,問問題的時候就不該該變動答案。 更正式點,方法僅僅在它們是顯示引用而且沒有其它做用時返回一個值。"(維基百科),由於這種方法咱們能夠劃分爲兩個設置:架構

  • 命令 -改變對象或整個系統的狀態(有時稱爲修飾符或賦值函數)。
  • 查詢 -返回結果,並不會改變對象的狀態。

In a real situation it is pretty simple to tell which is which. The queries will declare return type, and commands will return void. This pattern is broadly applicable and it makes reasoning about objects easier. On the other hand, CQRS is applicable only on specific problems.app

在實際環境中它是很是簡單的分辨出哪一個是哪一個。查詢將聲明返回類型,而命令將返回void。這種模式是普遍適用的,它使對象的推理更容易。另外一方面,CQRS只適用於特定的問題。asp.net

Many applications that use mainstream approaches consists of models which are common for read and write side. Having the same model for read and write side leads to a more complex model that could be very difficult to be maintained and optimized.dom

 

許多應用程序使用主流方法包括對models一般的讀和寫操做。對相同的model的讀取和寫入會致使一個更復雜的模型,可能很難被維護和優化.ide

The real strength of these two patterns is that you can separate methods that change state from those that don't. This separation could be very handy in situations when you are dealing with performance and tuning. You can optimize the read side of the system separately from the write side. The write side is known as the domain. The domain contains all the behavior. The read side is specialized for reporting needs.函數

這兩種模式的真正做用是你不能用分開的方法改變他們狀態。當你正在處理的性能和調優的狀況下多是很是方便,你能夠分開寫入端去優化系統的讀取端。寫入端被做爲一個領域(domain)。 這個領域包含的全部行爲。讀取端是專門用於報告需求.性能

Another benefit of this pattern is in the case of large applications. You can split developers into smaller teams working on different sides of the system (read or write) without knowledge of the other side. For example developers working on read side do not need to understand the domain model.學習

 

這種模式的另外一個好處是在大型的應用程序的狀況下,您能夠分割成更小的團隊開發工做(讀或寫),而不須要關注另外一側的工做。例如讀取端的開發人員不須要了解領域域模型。

Query side

The queries will only contain the methods for getting data. From an architectural point of view these would be all methods that return DTOs that the client consumes to show on the screen. The DTOs are usually projections of domain objects. In some cases it could be a very painful process, especially when complex DTOs are requested.

Using CQRS you can avoid these projections. Instead it is possible to introduce a new way of projecting DTOs. You can bypass the domain model and get DTOs directly from the data storage using a read layer. When an application is requesting data, this could be done by a single call to the read layer which returns a single DTO containing all the needed data.

Query 端

查詢將只包含獲取數據的方法。從架構的角度來看,這些將是在屏幕上顯示客戶消費返回DTOs(數據傳遞對象)的全部方法。DTOs直接來自使用一個read層的數據存儲。在某些狀況下,它多是一個很是痛苦的過程,尤爲是當要求複雜的DTO。

使用CQRS你能避免這些預測。相反,它是能夠引入一個新的DTO的投影方式。您能夠繞過域模型和DTO的直接使用讀出層從數據存儲。當一個應用程序請求數據,這多是由一個單向的調用read層返回單個包含全部須要的數據DTO。

The read layer can be directly connected to the database (data model) and it is not a bad idea to use stored procedures for reading data. A direct connection to the data source makes queries very easy to by maintained and optimized. It makes sense to denormalize data. The reason for this is that data is normally queried many times more than the domain behavior is executed. This denormalization could increase the performance of the application.

read層能夠直接鏈接到數據庫(數據模型),並使用存儲過程讀取數據並不算一個爛想法。直接鏈接到數據源的查詢很是容易維護和優化,這樣作的緣由是,數據一般是查詢執行屢次超過了領域的行爲,這種非規範化能夠提升應用程序的性能,非規範化的數據是有道理的。

Command side

Since the read side has been separated the domain is only focused on processing of commands. Now the domain objects no longer need to expose the internal state. Repositories have only a few query methods aside from GetById.

命令端

因爲讀取端已經被分開,doamin(也就是寫入端)只關注處理命令。如今的領域對象對象再也不須要公開的內部狀態。儲存庫除了除了GetById只有幾個查詢方法

 

Commands are created by the client application and then sent to the domain layer. Commands are messages that instruct a specific entity to perform a certain action. Commands are named like DoSomething (for example, ChangeName, DeleteOrder ...). They instruct the target entity to do something that might result in different outcomes or fail. Commands are handled by command handlers.

命令經過客戶端應用程序建立而後發送到domain層.命令就是指示特定的實體來執行特定的動做的消息。命令被命名爲像DoSomething (例如,ChangeName,DeleteOrder ...)。他們指示目標實體去作一些可能會致使不一樣的結果或者失敗的事情。命令集 command handlers操做。

public interface ICommand
{
    Guid Id { get; }
}

public class Command : ICommand
{
    public Guid Id { get; private set; }
    public int Version { get; private set; }
    public Command(Guid id,int version)
    {
        Id = id;
        Version = version;
    }
}
 
public class CreateItemCommand:Command
{
    public string Title { get; internal set; }
    public string Description { get;internal set; }
    public DateTime From { get; internal set; }
    public DateTime To { get; internal set; }

    public CreateItemCommand(Guid aggregateId, string title, 
        string description,int version,DateTime from, DateTime to)
        : base(aggregateId,version)
    {
        Title = title;
        Description = description;
        From = from;
        To = to;
    }
}

All commands will be sent to the Command Bus which will delegate each command to the command handler. This demonstrates that there is only one entry point into the domain. The responsibility of the command handlers is to execute the appropriate domain behavior on the domain. Command handlers should have a connection to the repository to provide the ability to load the needed entity (in this context called Aggregate Root) on which behavior will be executed.

全部命令將被髮送到命令總線,它委託Command handler處理每個Command 。這代表,領域(domain)只有一個入口點。Command handlers應該有一個鏈接到存儲庫提供加載所需的實體的能力(在這種上下文稱爲聚合根)。

public interface ICommandHandler<TCommand> where TCommand : Command
{
    void Execute(TCommand command);
}

public class CreateItemCommandHandler : ICommandHandler<CreateItemCommand>
{
    private IRepository<DiaryItem> _repository;

    public CreateItemCommandHandler(IRepository<DiaryItem> repository)
    {
        _repository = repository;
    }

    public void Execute(CreateItemCommand command)
    {
        if (command == null)
        {
            throw new ArgumentNullException("command");
        }
        if (_repository == null)
        {
            throw new InvalidOperationException("Repository is not initialized.");
        }
        var aggregate = new DiaryItem(command.Id, command.Title, command.Description,             
                                      command.From, command.To);
        aggregate.Version = -1;
        _repository.Save(aggregate, aggregate.Version);
    }
}

The command handler performs the following tasks:

  • It receives the Command instance from the messaging infrastructure (Command Bus)
  • It validates that the Command is a valid Command
  • It locates the aggregate instance that is the target of the Command.
  • It invokes the appropriate method on the aggregate instance passing in any parameter from the command.
  • It persists the new state of the aggregate to storage.

    命令處理程序執行如下任務:

  • 從messaging infrastructure(命令總線)接收命令實例。
  • 驗證該命令是不是一個有效的命令
  • 命令的目標是聚合實例(aggregate instance)。
  • 從command對象中傳遞任意參數調用做用在聚合實例上的適當的方法。
  • 存儲aggregate 的新狀態繼續存在。

Internal Events

The first question we should ask is what is the domain event. The domain event is something that has happened in the system in the past. The event is typically the result of a command. For example the client has requested a DTO and has made some changes which resulted in a command being published. The appropriate command handler has then loaded the correct Aggregate Root and executed the appropriate behavior. This behavior raises an event. This event is handled by specific subscribers. Aggregate publishes the event to an event bus which delivers the event to the appropriate event handlers. The event which is handled inside the aggregate root is called an internal event. The event handler should not be doing any logic instead of setting the state.

內部事件

咱們的第一個問題應該問什麼是域事件。域事件是指在系統中過去已經發生的一些事件。事件一般是一個命令的結果。例如,客戶端請求一個DTO作出一些的變化,致使被公佈在命令中。而後相應的命令處理程序加載正確的Aggregate root並執行適當的行爲。這種行爲引起一個事件。此事件是由特定的用戶處理的。Aggregate 發佈事件到事件總線併爲該事件分派適當的處事件。這是中內部操做聚合根的事件被稱爲內部事件。該事件處理程序不該該作任何邏輯替狀態設置。

Domain Behavior

域行爲

public void ChangeTitle(string title)
{
    ApplyChange(new ItemRenamedEvent(Id, title));
}

Domain Event

域事件

public class ItemCreatedEvent:Event
{
    public string Title { get; internal set; }
    public DateTime From { get; internal set; }
    public DateTime To { get; internal set; }
    public string Description { get;internal set; }

    public ItemCreatedEvent(Guid aggregateId, string title ,
        string description, DateTime from, DateTime to)
    {
        AggregateId = aggregateId;
        Title = title;
        From = from;
        To = to;
        Description = description;
    }
}

public class Event:IEvent
{
    public int Version;
    public Guid AggregateId { get; set; }
    public Guid Id { get; private set; }
}

Internal Domain Event Handler

內部域事件處理程序

public void Handle(ItemRenamedEvent e)
{
    Title = e.Title;
}

Events are usually connected to another pattern called Event Sourcing (ES). ES is an approach to persisting the state of an aggregate by saving the stream of events in order to record changes in the state of the aggregate.

事件一般是鏈接到另外一個叫作 Event Sourcing(ES)模式。ES是一種經過事件流保持保存aggregate狀態,以記錄更改aggregate狀態的方法。

As I mentioned earlier, every state change of an Aggregate Root is triggered by an event and the internal event handler of the Aggregate Root has no other role than setting the correct state. To get the state of an Aggregate Root we have to replay all the events internally. Here I must mention that events are write only. You cannot alter or delete an existing event. If you find that some logic in your system is generating the wrong events, you must generate a new compensating event correcting the results of the previous bug events.

正如我前面提到的,的每個Aggregate Root狀態的變化都是由事件觸發的內部事件操做Aggregate Root除了設置正確的狀態沒有其餘做用。爲了獲得一個Aggregate Root的狀態,咱們不得不重播全部內部的事件。在這裏我必須提到,事件是隻寫的。你不能改變或刪除現有的事件。若是您發現您的系統中產生一些邏輯錯誤的事件,您必須生成一個新的補償事件,糾正之前的錯誤事件的結果。

External Events

External events are usually used for bringing the reporting database in sync with the current state of the domain. This is done by publishing the internal event to outside the domain. When an event is published then the appropriate Event Handler handles the event. External events can be published to multiple event handlers. The Event handlers perform the following tasks:

  • It receives an Event instance from the messaging infrastructure (Event Bus).
  • It locates the process manager instance that is the target of the Event.
  • It invokes the appropriate method of the process manager instance passing in any parameters from the event.
  • It persists the new state of the process manager to storage.

But who can publish the events? Usually the domain repository is responsible for publishing external events.

外部事件

外部事件一般用於報告數據庫當前同步域的狀態。他所作的就是發佈內部事件到外域。當事件被髮布時相應的事件處理程序處理該事件。外部事件能夠發佈到多個事件處理程序。事件處理程序執行如下任務:

  • 從messaging infrastructure收到一個事件實例(事件總線)。
  • 事件的目標是定位進程管理器實例。
  • 進程管理器實例從事件中傳遞何任意參數調用適當的方法。
  • 繼續存儲進程管理器的新狀態。

但是,是誰發佈的事件的?一般狀況下,領域倉庫負責外部事件發佈。

 剛瀏覽下這書,感受蠻不錯的,比《Pro asp.netMVC3》質量要好不少,沒學過的童鞋能夠天天學習1到2章,看不懂的問題在羣論壇留言,你們積極參與,之後羣裏就以MVC爲主要話題。

相關文章
相關標籤/搜索