本文章是由機器翻譯。

技術最前線

CQRS 共同申請

Dino Esposito

Dino Esposito領域驅動設計 (DDD) 浮出水面大約十年前,鼓舞人心的軟體發展人員和架構師。超越其混凝土的優點和缺點,DDD 體現了從早期的物件導向范式的任何人都可以涉及舊夢:建設各地全面的物件模型的應用,以解決所有利益相關者的要求和關切的夢想。

在過去十年裡,許多開發人員開始遵循 DDD 準則的專案中。一些專案是成功的有些沒有。事實是無所不包的物件模型,以涵蓋軟體系統的任何功能或無法正常工作的方面是一個美好的烏托邦。特別是在先進 UX,頻繁地改變商業模式和擺動要求這些瘋狂的日子,一個堅實、 穩定的物件模型實際上是一種錯覺。

最近,另一種方法的另一個首字母縮略詞開始蓄勢待發 — — 指揮和查詢責任分離 (CQRS)。Cqrs 體系並不是最新的酷玩具的軟體專家。它不是甚至最多可用示例表示的一樣複雜。簡單地說,CQRS 是具體的實施模式,可能最適合幾乎任何類型的軟體應用程式,預期的壽命和複雜性。

有不只一種方式做 CQRS 的 — — 那裡至少三種不同口味。你甚至可以昵稱他們之後的酒店客房和飲料的共同行銷規則:定期、 高檔和豪華。做一個快速搜尋 CQRS 例子和文章,你會發現大多數屬於豪華的類別。這實際上是太複雜,矯枉過正為最常見的應用程式。

CQRS 是回報專案的認知複雜性無關的軟體發展方法。在年底的一天,CQRS 常見的應用程式是簡單返工經典的多層體系結構,為更有影響力的變化和演變敞開了大門。

命令和查詢

同時開發程式設計語言在八十年代末的艾菲爾鐵塔,貝特朗邁耶得出結論軟體具有改變系統狀態和讀取系統狀態的查詢的命令。任何軟體語句應命令或查詢 — — 不能是兩者的組合。還有一個更好的方式來表達了相同的概念:問一個問題,不應該改變的答案。CQRS 是較近的重新表述的相同的核心原則:命令和查詢是不同的東西,應分別實施。

命令與查詢的邏輯分離並不看得很清楚是否兩組操作被迫使用相同的程式設計堆疊和相同的模型。這是尤其如此,在複雜的業務場景。一種單一模式 — — 是否物件模型、 功能模型或別的 — — 可以迅速變得難以管理。模型生長指數龐大和複雜,以吸收時間和預算,但它永遠不會工作它應該的方式。

Cqrs 體系所追求的分離被通過對一個圖層中的查詢操作和另一個層中的命令進行分組。每一層有其自己的資料,其自己的服務集的模型,並建立使用它自己的模式和技術的組合。更重要的是,這兩個層甚至可能是在兩種截然不同,分別進行優化,而互相影響。圖 1為 CQRS 體系結構奠定基礎。

規範和多-­分層 CQRS 體系結構
圖 1 規範和多-­分層 CQRS 體系結構

只承認命令和查詢都兩個不同的事情對軟體體系結構具有深刻的影響。例如,突然變得簡單設想,每個域層的代碼。域層命令堆疊中的僅涉及執行任務所需的資料和業務和安全規則。域層查詢堆疊,在另一方面,可能是一樣簡單直接的 SQL 查詢通過ADO.NET連接。

當您在演示文稿門口將安全檢查時,查詢堆疊成為Entity Framework或用於查詢資料的瘦包裝。每個域在層內,你有空也到與域需要密切相似的圖形資料沒有複製或複製資料,以適應斑演示文稿和業務需求。

DDD 最初出現時,它是關於處理核心軟體發展中的複雜性。一路走來,雖然,從業人員面臨複雜的負載。許多人認為它是簡單的業務域的一部分。相反,大多數複雜性導致從笛卡兒積的查詢和命令。分離命令從查詢通過一個數量級來減小複雜性。粗糙的數學術語,您可以比較 CQRS 和 N + N 與 NxN 的綜合域模型方法。

你是如何開始做 CQRS 的?

你可以把基本的創建、 讀取、 更新、 刪除 (CRUD) 系統進入 CQRS 啟發的系統。假設你有規範的ASP.NETMVC Web 應用程式收集使用者資料,以各種形式顯示。這是大多數應用程式所做的和任何的建築師知道如何快速而有效地生成。您可以重寫這與 CQRS 銘記。你會驚訝地看到所需的更改很少,但你可以得到的好處是潛在的無限。

你典型的系統組織的圖層。你有直接從編排用例的控制器調用的應用程式服務。應用程式服務 (通常稱作工人服務) 住在 Web 服務器--並排與控制器。關於圖 1,應用程式服務形成應用程式層。應用程式層是從中你運行命令和查詢對系統的其餘部分的平臺。應用 CQRS 意味著您將使用兩個不同的中介層。一層照顧改變系統狀態的命令。另檢索的資料。圖 2 體系結構關係圖顯示上一個樣例ASP.NETMVC 專案。

ASP.NETMVC 專案 CQRS 體系結構
圖 2 ASP.NETMVC 專案 CQRS 體系結構

創建類庫專案夫婦 — — 查詢堆疊和命令堆疊 — — 和引用都從主要的 Web 服務器專案。

查詢堆疊

查詢堆疊類庫是只關注資料檢索。它使用的資料模型相匹配盡可能密切在展示層中使用的資料。你幾乎不需要任何商務規則在這裡,因為他們通常應用於改變狀態的命令。

域模型模式由 DDD 流行是本質上是一種組織方式的域邏輯。當你從前端的查詢時,你只處理應用程式邏輯的一部分,用例。長期業務邏輯通常會導致從特定于應用程式邏輯與不變域邏輯的結合。一旦你知道持久性格式和演示文稿格式,你做的一切是地圖資料,如同你在平原老ADO.NET了 /SQL 查詢。

很有用,記得你可以從應用程式層調用的任何代碼表示系統的業務領域。因此,它是不變的 API,表示系統的核心邏輯。理想情況下,您應該確保沒有不一致和不一致的行動,甚至可能通過公開的 API。因此,執行查詢堆疊中,唯讀性圍繞要連接到的資料庫的預設Entity Framework上下文物件的包裝類中添加下面的代碼所示:

public class Database : IDisposable
{
  private readonly QueryDbContext _context = new QueryDbContext();
  public IQueryable<Customer> Customers
  {
    get { return _context.Customers; }
  }
  public void Dispose()
  {
   _context.Dispose();
  }
}

比賽類是作為 DbSet < T > 實現的。 在 DbCoNtext 基類中的集合。如,它提供了對底層資料庫的完全存取權限,而您可以使用它來設置查詢和更新操作通過LINQ到實體。

設立了一個查詢管道的基本步允許查詢僅對資料庫的訪問。這是包裝類,該類的作用在哪裡作為 IQueryable < T > 公開比賽。應用程式層將使用資料庫的包裝類來實現旨在使演示文稿的資料的查詢:

var model = new RegisterViewModel();
using (var db = new Database())
{
  var list = (from m in db.Customers select m).ToList();
  model.ExistingCustomers = list;
}

現在是演示文稿與資料來源之間的直接連接。你真的只閱讀和格式化資料顯示的目的。你指望門口通過登錄和使用者介面約束強制的授權。如果不是這樣,不過,您可以添加更多的層,一路走來並啟用通過 IQueryable 資料集合的資料交換。資料模型是資料庫相同,是 1 比 1 與持久性。這種模型有時被稱為層狀的運算式目錄樹 (讓)。

有幾件事情你應該注意到在這一點上。首先,你在讀的管道在商務規則通常不存在。所有你可能在這裡的授權規則和篩選器。在應用程式層一級,這些是知名的。你有沒有需要處理的資料傳輸物件一路走來。你有一個持久性模型和視圖的實際資料容器。應用程式服務,在您結束與以下模式:

var model = SpecificUseCaseViewModel();
model.SomeCollection = new Database()
     .SomeQueryableCollection
     .Where(m => SomeCondition1)
     .Where(m => SomeCondition2)
     .Where(m => SomeCondition3)
     .Select(m => new SpecificUseCaseDto
       {
         // Fill up
       })
     .ToList();
return model;

你發現在代碼片段中的所有資料傳輸物件都是特定于您正在執行的演示文稿用例。他們是使用者想要在您正在構建的剃刀視圖中看到,這些類是不可避免的。此外,您可以替換所有的 Where 從句特設 IQueryable 擴充方法和輪到整個對話方塊中特定于域的語言編寫的代碼。

第二件事要注意有關查詢堆疊事關持久性。CQRS 最簡單的形式,命令和查詢堆疊共用同一個資料庫。此體系結構使得 CQRS 經典的 CRUD 系統相似。這使得更容易採取當人們抗拒改變的時候。然而,您可以設計的後端,所以命令和查詢堆疊有其自己的資料庫優化為其特定的目的。然後同步兩個資料庫成為另一個問題。

命令堆疊

在 CQRS,命令堆疊僅涉及與執行修改應用程式狀態的任務。應用程式層從演示文稿接收請求,並通過管道向推命令號令執行。"推到管線的命令"的表達是各種流派的 CQRS 原產地。

在最簡單的情況下,推命令組成簡單的調用事務腳本。觸發一個普通的工作流,完成任務所需的所有步驟。從應用程式層推命令可以簡單到如下面的代碼:

public void Register(RegisterInputModel input)
{
  // Push a command through the stack
  using (var db = new CommandDbContext())
  {
    var c = new Customer {
      FirstName = input.FirstName,
      LastName = input.LastName };
    db.Customers.Add(c);
    db.SaveChanges();
  }
}

如果需要,你可以屈服控制到真正域層提供的服務和域模型在哪裡您執行完整的業務邏輯。然而,使用 cqrs 體系並不一定把你們束縛于 DDD 和骨料、 工廠和值物件的事情。你可以有一個域模型無額外的複雜性的命令/查詢分離的好處。

超越常規 CQRS

事實上你可以優化命令和查詢管道在 CQRS 謊言的力量將沒有風險,優化一個可能打破對方。Cqrs 體系最基本的形式使用一、 共用資料庫和不同庫呼籲讀取並寫入從應用程式層。

更為複雜的形式可能會有多個資料庫、 通曉多國語言的持久性、 事件採購和更重要的是,查詢用於資料非正常化更靈活的方式把命令交給後端。這是更為靈活,因為通過匯流排發送命令,並將事件發佈允許您定義和修改任何任務一樣,類似于管理流程圖。同時,您可以擴展幾乎在哪裡,你必須要將電源和功能添加到匯流排元件。

許多開發人員稱讚 cqrs 體系,但傾向于限制適用于協作和高規模的應用程式。Cqrs 體系並不是頂級的建築,是技術不可知論者。某種程度上,CQRS 是甚至不可知的設計模式,但它是一種模式本身。它是簡單、 功能強大、 恰到好處的常見應用程式。


Dino Esposito 合著的"Microsoft.NET:構建企業應用程式"(微軟出版社,2014年) 和"程式設計ASP.NETMVC 5"(微軟出版社,2014年)。Microsoft.NET 框架和 Android 平臺,它也會經常在世界各地的業內活動中發表演講的技術傳教士,埃斯波西托分享他視覺軟體 software2cents.wordpress.com 和在 Twitter 上 twitter.com/despos

感謝以下技術專家對本文的審閱:喬恩 Arne Saeteras