アグリゲートがビジネス上の意思決定を行うために必要とする情報は、アグリゲートの状態の一部として保存する必要があります。そのため、クライアントのアカウントにお金を入金するコマンドを受信すると、そのクライアントの現在の/更新状態が既にあるはずです。これには、各アカウントの現在の残高を含めることができます。
また、情報を取得するために集計が読み取りモデルに移動しないようにすることもお勧めします。達成しようとしていることに応じて、読み取りモデル (状態は重要ではない) からの追加の詳細でコマンドを充実させることができますが、集計自体はそれ自体の既知の状態から取得する必要があります。
編集
質問を読み直した後、複数の集計にまたがる状態の追跡について話していることがわかりました。これはサガの領域に入ります。上位 10% に入るために必要なしきい値を追跡するサガを作成できます。したがって、クライアントが預金を行うたびに、サガはこれがランキングのどこに配置されるかを追跡できます。そのクライアントがスレッドホールドを超えた場合、サガからコマンドを発行して、必要な基準を満たしていることを示すことができます。
あなたのケースでは、saga はすべての預金の合計額を追跡する可能性があるため、預金が行われると、クライアントが上位 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?
}