2011-08-15 18 views
77

如果我使用RDBMS(例如SQL Server)來存儲事件源數據,模式可能是什麼樣子?使用RDBMS作爲事件源代碼存儲

我已經看到抽象意義上談到的一些變體,但沒有具體的。例如,假設有一個「產品」實體,並且對該產品的更改可以採用以下形式:價格,成本和說明。我感到困惑是否我倒是:

  1. 有一個「ProductEvent」表中,有一個產品,每個變化意味着該表中的新記錄的所有字段,以及「誰,什麼,在哪裏,爲什麼,何時以及如何「酌情。當成本,價格或描述發生變化時,會添加一個新行來表示產品。
  2. 將產品成本,價格和描述存儲在通過外鍵關係連接到產品表的單獨表中。在對這些屬性進行更改時,根據需要使用WWWWWH編寫新行。
  3. 在「ProductEvent」表中存儲WWWWWH以及表示事件的序列化對象,這意味着事件本身必須在我的應用程序代碼中加載,反序列化並重新播放,以便重新構建應用程序狀態給定的產品。

特別是我擔心上面的選項2。極端而言,產品表幾乎是每個屬性一個表,在哪裏加載給定產品的應用程序狀態將需要從每個產品事件表加載該產品的所有事件。這張桌子爆炸對我來說是錯誤的。

我確定「這取決於」,雖然沒有任何單一的「正確答案」,但我試圖理解什麼是可以接受的,什麼是完全不可接受的。我也知道NoSQL在這裏可以提供幫助,在這裏可以根據聚合根存儲事件,這意味着只有對數據庫的單個請求來獲取事件來重建對象,但是我們沒有使用NoSQL db這一刻,我感覺周圍的替代品。

+2

以其最簡單的形式:[Event] {AggregateId,AggregateVersion,EventPayload}。不需要聚合類型,但可以選擇存儲它。不需要事件類型,但可以選擇存儲它。這是發生事情的一長串清單,其他任何事情都只是優化。 –

+7

絕對遠離#1和#2。將所有內容序列化爲一個blob並將其存儲。 –

回答

83

事件存儲不應該需要了解事件的特定字段或屬性。否則,對模型的每一次修改都將導致不得不遷移數據庫(就像在老式的基於狀態的持久性中一樣)。因此我根本不會推薦選項1和2。

以下是在Ncqrs中使用的模式。如您所見,表「Events」將相關數據存儲爲CLOB(即JSON或XML)。這與您的選項3相對應(只有沒有「ProductEvents」表,因爲您只需要一個通用的「Events」表。在Ncqrs中,通過「EventSources」表映射到您的Aggregate Roots,其中每個EventSource對應於一個實際聚合根)

Table Events: 
    Id [uniqueidentifier] NOT NULL, 
    TimeStamp [datetime] NOT NULL, 

    Name [varchar](max) NOT NULL, 
    Version [varchar](max) NOT NULL, 

    EventSourceId [uniqueidentifier] NOT NULL, 
    Sequence [bigint], 

    Data [nvarchar](max) NOT NULL 

Table EventSources: 
    Id [uniqueidentifier] NOT NULL, 
    Type [nvarchar](255) NOT NULL, 
    Version [int] NOT NULL 

Jonathan Oliver's Event Store implementation的SQL持久性機制基本上由所謂的「承諾」與BLOB字段「有效載荷」一個表。這與Ncqrs幾乎相同,只是它以二進制格式(例如,增加了加密支持)序列化事件的屬性。

Greg Young建議採用類似的方法,如extensively documented on Greg's website

他的典型的「事件」表的架構如下:

Table Events 
    AggregateId [Guid], 
    Data [Blob], 
    SequenceNumber [Long], 
    Version [Int] 
+4

不錯的答案!我一直在閱讀的有關使用EventSourcing的主要觀點之一是查詢歷史記錄的能力。我將如何製作一個報告工具,以便在所有有趣的數據被序列化爲XML或JSON時查詢。是否有任何有趣的文章尋找基於表格的解決方案? –

+5

@MarijnHuizendveld你可能不想查詢事件存儲本身。最常見的解決方案是連接幾個事件處理程序,將事件投影到報告或BI數據庫中。對這些處理程序重播事件歷史記錄。 –

+1

@Denis Traub感謝您的回答。爲什麼不針對事件存儲本身進行查詢?我擔心如果每次我們提出一個新的商業智能案例時必須重播完整的歷史,它會變得相當混亂/激烈? –

3

那麼你可能想給看看Datomic。

Datomic是靈活的,基於時間的事實,支持查詢數據庫和連接,彈性可擴展性和ACID事務。

我寫了一個詳細的解答here

您可以觀看斯圖爾特哈洛韋解釋Datomic here

的設計,因爲在時間Datomic商店事實談話,你可以使用它的事件採購使用情況,並這麼多。

1

可能的提示是設計,其次是「漸變維度」(類型= 2)應該可以幫助您覆蓋:

  • 發生的事件(通過代理鍵)每個狀態的
  • 耐久性的順序(有效從 - 有效到)

左摺疊函數應該也可以實現,但是您需要考慮將來的查詢複雜性。

2

GitHub項目CQRS.NET有幾個具體的例子說明如何在幾種不同的技術中使用EventStores。在撰寫本文時,有一個實現SQL using Linq2SQL和一個SQL schema去與它,MongoDBDocumentDB(CosmosDB,如果你在Azure)一個和一個使用EventStore(如上所述)。 Azure中有更多表單存儲和Blob存儲,這與平面文件存儲非常相似。

我想這裏的主要觀點是他們都符合相同的委託人/合同。它們都將信息存儲在一個地方/容器/表格中,它們使用元數據來識別來自另一個事件的事件,並且「僅」存儲整個事件 - 在某些情況下序列化,支持技術,就像它一樣。因此,根據您選擇文檔數據庫,關係數據庫還是平面文件,可以通過幾種不同的方式實現事件存儲的相同意圖(如果您隨時改變主意並發現需要遷移或支持多個存儲技術)。

作爲該項目的開發人員,我可以分享一些我們所做選擇的見解。

首先,我們發現(即使使用唯一的UUID/GUID而不是整數),出於多種原因,出於策略原因出現順序ID,因此只有一個ID對於一個鍵來說不夠獨特,所以我們合併了我們的主ID鍵列用數據/對象類型來創建應該是真正的(就應用程序而言)唯一鍵的內容。我知道有些人說你不需要存儲它,但這取決於你是否是綠地或與現有系統共存。

由於可維護性原因,我們堅持使用單個容器/表/集合,但是我們確實爲每個實體/對象使用了單獨的表格。我們在實踐中發現,這意味着應用程序需要「創建」權限(通常來說,這不是一個好主意......通常總是有例外/排除),或者每當新的實體/對象出現或部署時,新的存儲容器/表/集合需要被製作。我們發現這對本地開發來說太痛苦了,而且對於生產部署來說也是有問題的。你可能不會,但那是我們的真實世界的經驗。

要記住的另一件事是要求動作X發生可能會導致許多不同的事件發生,從而知道命令/事件/有用的所有事件。它們也可以跨越不同的對象類型,例如推動購物車中的「購買」可能會觸發帳戶和倉儲事件觸發。消費應用程序可能想知道所有這些,所以我們添加了一個CorrelationId。這意味着消費者可以要求所有由於他們的請求而引發的事件。你會在schema中看到。

特別是對於SQL,我們發現如果索引和分區沒有被充分使用,性能真的成爲瓶頸。如果您使用快照,記住事件將需要以相反的順序進行流式傳輸。我們嘗試了幾個不同的索引,發現在實踐中,需要一些額外的索引來調試真實世界的應用程序。再次,你會在schema中看到。

其他生產中元數據在基於生產的調查中很有用,時間戳讓我們洞察事件持續發生的順序。這爲我們提供了一些特別重大的事件驅動系統方面的一些幫助,這些系統引發了大量的事件,爲我們提供了有關網絡和網絡系統分佈等性能的信息。

+0

太好了,謝謝。碰巧,自從寫這個問題以來,我已經在github上創建了一些自己的Inforigami.Regalo庫。 RavenDB,SQL Server和EventStore實現。想知道如何做一個基於文件的文件,併發笑。 :) –

+1

乾杯。我主要爲其他在近期遇到的人提供答案,並分享一些經驗教訓,而不僅僅是結果。 – cdmdotnet

相關問題