2016 年 8 月

第 31 卷,第 8 期

本文章是由機器翻譯。

技術最前線-CRUD 之外︰ 命令、事件及匯流排

Dino Esposito |2016 年 8 月

Dino Esposito在最近期專欄中,我討論了如何建立歷程記錄的建立、 讀取、 更新、 刪除 (CRUD H)。H CRUD 是傳統的 CRUD 簡單擴充功能,您使用兩個在概念上不同的資料存放區來保存目前狀態的物件和個別物件的存留期間發生的所有事件。如果您只會限制您的願景,其中包含目前狀態的資料存放區,然後是與傳統的 CRUD 幾乎相同。您有客戶記錄、 發票、 訂單和其他任何形成商務網域的資料模型。

重點將在下面此摘要資料存放區不是主要的資料存放區建立,但是衍生為投影的事件資料存放區。換句話說,建置歷史 CRUD 的本質是儲存事件,因為它們會發生此問題,而且然後推斷系統的目前狀態,您需要建立任何 ui。

 設計商務事件周圍解決方案是相對較新的方法取得動量,不過長辦法直接它成為主流典範。將您的設計,在事件上置中是很有幫助,因為您不會錯過系統中發生的任何項目您可以重新讀取和重新執行事件,在任何時間和建置新預測,相同的核心資料,例如商務智慧工作的上方。更有趣的是,事件中當做架構設計人員,有了您系統的特定業務無所不在語言周圍設計最大的機會。超出正在支柱的 Domain-Driven 設計 (DDD),更 pragmatically 無所不在語言一點無異是如虎添翼解周圍的商業網域,並規劃相互合作的組件的最有效的架構圖和內部動態工作和工作流程。

您可能會看到在我的事件實作 (msdn.com/magazine/mt703431) 和 2016 年 6 月 (msdn.com/magazine/mt707524) 資料行是非常簡單,而且在某個程度更簡單。主要的目的,,已經顯示任何 CRUD 無法開啟到最少的力氣 H CRUD,且仍然獲得一些好處,從商務事件簡介。H CRUD 方法有一些明顯的重疊受歡迎的首字母縮略字和關鍵字的今天,例如應用 CQRS 與事件來源。在本專欄中,我將介紹 H CRUD 更進一步的概念,讓它與核心的構想的事件來源合併。您會看到如何 H CRUD 可以轉換成命令、 匯流排和事件,在第一次看起來可能像基本過於複雜的方法來讀取和寫入資料庫所做的實作。

一個事件,許多彙總

在我看來,其中一個原因軟體有時候很難寫入時間,而且預算會注意到網域專家的商業語言缺乏。大部分的情況下,認可需求表示將了解的需求對應至某種形式的關聯式資料模型。商務邏輯則是持續性和簡報,進行任何必要的調整方式之間的通道資料架構。While 完美,此模式有效一段時間且修訂版層次的複雜度,進行不實際的案例數目會是數字不相關,並,前往的 DDD,編寫仍然是目前處理的任何軟體專案的最有效方式。

因為它們會強制不同格式的網域,更多工作導向的而不會使用用來儲存資料的完整關聯式模型的急迫性的分析,在此有幫助事件。當您檢視事件時,不過,基數為索引鍵。在過去的資料行中所討論 H CRUD 範例中,我做假設,可能是相當危險的 if 放開而不需要進一步的考量和說明。在範例中,我使用事件來彙總一對一關聯。事實上,我使用彙總保存當做外部索引鍵的唯一識別碼來連結事件。要使用的發行項,則每當聊天室已登記,系統會記錄的預約建立的事件,是指給定的登記識別碼。若要擷取所有事件的彙總 (也就是登記) 上指定的登記識別碼的事件資料存放區的查詢已足夠取得所有的資訊。明確地運作,但這就是相當簡單的情況。危險是一個簡單的案例層面而常見的作法,您通常會將從簡單的解決方案簡單的解決方案。這不是完全好事。

彙總和物件

真實事件/彙總關聯的基數是無所不在商務領域的語言撰寫。無論如何,一對多關聯是很有可能發生比簡單的一對一關聯。具體而言,一個對多關聯之間事件有時可能會與多個彙總相關,和一個以上的彙總可能會想要處理該事件,且可能有其狀態變更,因為該事件的事件和彙總方法。

例如,想像一下,在其中發票系統中註冊為執行中的工作訂單成本。這表示在網域模型中,您可能有兩個彙總 — 發票和工作順序。已註冊的事件發票擷取發票彙總感興趣,因為系統中,輸入新的發票,但它也可能會擷取 JobOrder 彙總的注意,如果發票指的是一些與順序相關的活動。很明顯地,是否發票與相關的工作順序是否可以決定才完全了解商務網域。可能會有網域模型 (和應用程式) 中的發票可能會獨立獲得及網域模型 (及應用程式) 中發票可能會登錄在工作的帳戶處理訂單之後變更的目前餘額。

不過,取得點完全許多彙總與事件可能會變更方案的架構和更可行技術型態而運作。

分派事件打破複雜度

為基礎的 CRUD 和 H CRUD 出大量的條件約束事件繫結的單一彙總。商務事件所接觸到多個彙總,當您撰寫商務邏輯程式碼,以確保狀態已變更和適當的追蹤。當彙總事件數目超過重大閾值時,商務邏輯程式碼的複雜度可能困難且不實際處理及發展。

在此情況下,CQRS 模式代表正確的方向的第一步正如它基本上是 「 只讀取 」 或 「 只是變更 」 動作上分別原因系統的目前狀態。事件 Sourcing 是建議您記錄為事件系統中所發生的另一個常見模式。追蹤整個系統的狀態,而且系統中的彙總的實際狀態會建置為投影的事件。換句話說,對應事件的內容完全形成軟體中可用的物件狀態的其他屬性。事件 Sourcing 是根據知道如何儲存和擷取事件的架構。事件來源的機制是附加專用、 支援重新執行事件資料流並知道如何儲存可能會有極大的不同的版面配置的相關的資料。

事件存放區架構,例如 EventStore (bit.ly/1UPxEUP) 和 NEventStore (bit.ly/1UdHcfz) 抽離真正的持續性架構,並提供一個超級 API 程式碼直接使用事件處理。在本質上,您會看到有些許相關的事件資料流,且這些事件的吸引力的點是彙總。這樣就可以正常運作。不過,當事件有多個彙總的影響,您應會看見一種方法讓每一個彙總所有其感興趣的事件追蹤的能力。此外,您應該只點事件的持續性之外,建置軟體基礎結構管理,可讓所有的常設彙總,以瞭解感興趣的事件。

若要達到適當分派事件彙總及適當的事件持續性的目標,H CRUD 是不夠的。必須重新檢查背後的商務邏輯和保存事件相關資料所使用的技術模式。

定義彙總

彙總的概念是來自 DDD,簡單地說它是指叢集的網域物件群組在一起以相符的交易一致性。交易的一致性是指,將彙總內包含的任何保證是一致和最新的商務動作的結尾。下列程式碼片段顯示摘要列出任何彙總類別的主要部分的介面。可能有更多,但是我敢說這是絕對的最小值︰

public interface IAggregate
{
  Guid ID { get; }
  bool HasPendingChanges { get; }
  IList<DomainEvent> OccurredEvents { get; set; }
  IEnumerable<DomainEvent> GetUncommittedEvents();
}

在任何時間,彙總包含發生的事件清單,並可區分這些認可與那些未認可暫止的變更會導致。實作 IAggregate 介面的基底類別會有非公用成員,才能設定 ID 和實作的認可及未認可的事件清單。此外,彙總的基底類別也會用來將事件加入內部清單未認可事件的一些 RaiseEvent 方法。有趣的事是如何事件在內部用來改變狀態的彙總。假設您有客戶彙總,並想要更新客戶的公用名稱。在 CRUD 案例中,它就會指派如下︰

customer.DisplayName = "new value";

與事件,它會是更複雜的路由︰

public void Handle(ChangeCustomerNameCommand command)
{
 var customer = _customerRepository.GetById(command.CompanyId);
 customer.ChangeName(command.DisplayName);
 customerRepository.Save(customer);
}

讓我們來跳過一下 Handle 方法,哪些人在實作上執行它和焦點。首先,它似乎 ChangeName 是檢查先前的 CRUD 樣式程式碼只包裝函式。嗯,不完全︰

public void ChangeName(string newDisplayName)
{
  var evt = new CustomerNameChangedEvent(this.Id, newDisplayName);
  RaiseEvent(e);
}

彙總的基底類別上定義的 RaiseEvent 方法只會附加內部未認可的事件清單中的事件。彙總保存時,最後會處理未認可的事件。

保存透過事件狀態

與事件精通,儲存機制類別的結構可以成為泛型。到目前為止所述的彙總類別運作而設計的儲存機制的 Save 方法只會執行迴圈的彙總未認可的事件和呼叫必須提供新的彙總方法的清單 — ApplyEvent 方法︰

public void ApplyEvent(CustomerNameChangedEvent evt)
{
  this.DisplayName = evt.DisplayName;
}

彙總的類別會有感興趣的每個事件的 ApplyEvent 方法的其中一個多載。CRUD 樣式程式碼被視為的方式後就會發現它的位置。

仍有另外一個遺漏連結︰ 如何協調前端的使用案例、 與多個彙總、 商務工作流程持續性的使用者動作? 您需要匯流排元件。

簡介匯流排元件

匯流排元件可定義為執行中的已知的商務程序的執行個體間共用的路徑。使用者做透過展示層,並設定系統在處理指示。應用程式層接收這些輸入,並將它們轉化成具體的商務動作。在 CRUD 案例中,應用程式層將會直接呼叫商務程序 (也就是工作流程) 負責要求的動作。

過多的彙總與商務規則時,在匯流排大幅簡化整體設計。應用程式層會推送到匯流排做出適當的接聽程式,命令或事件。接聽程式是通常稱為 「 巨作 」 的元件,最後是已知的商務程序的執行個體。傳奇知道如何回應一堆命令和事件。傳奇保存層存取,且可以推送命令和事件匯流排的備份。傳奇將是上述的控制代碼方法所屬的類別。通常每個工作流程的傳奇類別或使用案例和傳奇完全由事件,它可以處理的命令。整體結果的架構屬 [圖 1

使用匯流排分派事件和命令
[圖 1 使用匯流排分派事件和命令

最後,請注意,事件必須也是保存回來源查詢。這會引發另一個重要點︰ 傳統的關聯式資料庫很適合用來儲存事件的嗎? 可以隨時在開發和甚至 post 生產加入不同的事件。此外,每個事件,會有自己的結構描述。在此情況下,非關聯式資料存放區納入即使使用關聯式資料庫仍會保留選項 — 至少選項,請考慮並排除與強式的辨識項。

總結

我敢說的認知,大部分的軟體的複雜性是因為,我們保留在思考系統的 CRUD 方式,雖然基本的四個作業中的縮寫為基礎 (建立、 讀取、 更新、 刪除) 會單純讀取和寫入至單一資料表或彙總。本文旨在講師的模式和工具,將會繼續下個月當我出示嘗試要進行這類的開發更快速且持續性的架構更深入分析。


Dino Esposito 是每個 「 Microsoft.NET:  架構的企業應用程式 」 (Microsoft Press,2014年) 和 [使用 ASP.NET 最新 Web 應用程式] (Microsoft Press,2016年)。.NET 和 Android 平台在 JetBrains,並在世界各地的產業活動的技術推廣人員 Esposito 分享軟體的願景 software2cents.wordpress.com 和 Twitter: @despos

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