2012-12-11 30 views
10

有時我正在開發一個使用CQRS模式和事件採購的小型項目。 我有一個結構性問題,我不知道要採取哪種解決方案來解決它。CQRS /事件採購,如何獲得一致的數據來應用業務規則?

想象下面的例子: 一個命令與銀行客戶存入一定數量的錢(DepositCommand)的信息一起發送。 在命令處理程序/ Entity/Aggregate(對於討論不重要)中,必須應用業務規則; 如果客戶是賬戶中有更多錢的前10%之一贏得獎金。

問題是我怎樣才能得到最新的,一致的數據,以瞭解客戶存款後是否在前10%。

  • 我不能使用事件存儲,因爲不可能做出這樣的查詢;
  • 我不確定我是否可以使用讀取的模型,因爲它不是100% 確實是最新的。

如果您需要數據庫中的數據來應用業務規則,該怎麼辦?如果我不注意最新數據,我會遇到 給予兩個不同客戶的獎品

期待聽到您的意見。

回答

6

聚合需要做出業務決策的任何信息都應作爲聚合狀態的一部分進行存儲。因此,如果收到命令將資金存入客戶賬戶,您應該已經擁有該客戶的當前/更新狀態,該狀態可以包含每個賬戶的當前餘額。

我也建議聚合不應該去閱讀模型來獲取信息。根據你試圖達到的目標,你可以使用讀取模型中的其他細節(狀態不重要)豐富命令,但是聚合本身應該從它自己已知的狀態拉動。


編輯

重新閱讀的問題後,我意識到你是在談論跨多個聚集跟蹤狀態。這屬於一個傳奇的領域。您可以創建一個追蹤前10%所需閾值的傳奇。因此,無論客戶何時進行存款,該傳奇都可以跟蹤這些信息在排名中的位置。如果該客戶端跨越線程攔截器,則可以從該協調器發佈命令來表明它們符合所需的條件。

就您而言,您的傳奇可能會跟蹤所有存款的總金額,因此在存入存款時,您可以決定客戶現在是否處於前10%。您可能想要問自己的其他問題......如果客戶存入$ X的金額,並立即將$ Y放回到存儲下;應該發生什麼?等


很粗骨料/佐賀處理方法...

public class Client : Aggregate 
{ 
    public void Handle(DepositMoney command) 
    { 
     // What if the account is not known? Has insufficient funds? Is locked? etc... 
     // Track the minimum amount of state required to make whatever choice is required. 
     var account = State.Accounts[command.AccountId]; 

     // Balance here would reflect a point in time, and should not be directly persisted to the read model; 
     // use an atomic update to increment the balance for the read-model in your denormalizer. 
     Raise(new MoneyDeposited { Amount = command.Amount, Balance = account.Balance + command.Amount }); 
    } 

    public void Handle(ElevateClientStatus command) 
    { 
     // you are now a VIP... raise event to update state accordingly... 
    } 
} 

public class TopClientSaga : Saga 
{ 
    public void Handle(MoneyDeposited e) 
    { 
     // Increment the total deposits... sagas need to be thread-safe (i.e., locked while state is changing). 
     State.TotalDeposits += e.Amount; 

     //TODO: Check if client is already a VIP; if yes, nothing needs to happen... 

     // Depositing money itself changes the 10% threshold; what happens to clients that are no longer in the top 10%? 
     if (e.Balance > State.TotalDeposits * 0.10) 
     { 
      // you are a top 10% client... publish some command to do whatever needs to be done. 
      Publish(new ElevateClientStatus { ClientId = e.ClientId, ... }); 
     } 
    } 

    // handle withdrawls, money tranfers etc? 
} 
+1

謝謝您的回覆卡爾加里,你得到了我的觀點。我必須閱讀更多關於佐賀模式,然後我回復一個成形的意見。 –

+0

所以你正在聲稱我有一個與應用程序一起創建和處置的傳奇,就像一個單身人士。該傳奇追蹤存款總額。每個存錢的客戶都會增加這筆交易的總金額。存儲我使用事件存儲的總數?我問,因爲當我啓動應用程序時,我需要加載當前的總數。這有道理嗎?謝謝 –

+0

@JP - 傳奇將管理它自己的狀態。在某一時刻,您將創建一個「分行」或「銀行」,這可能會啓動傳奇以追蹤總存款。每當存款或其他相關事件發生時,傳奇狀態將被檢索(理想情況下緩存在內存中),以便它總是知道運行總數。如果您的應用程序需要知道其他原因的總數,那麼總數可能會成爲「bank」或「branch」聚合狀態的屬性。你的聚合/傳奇是所有數據的權威,你不想去閱讀這種類型的信息。合理? –