OOPコミュニティでは、クラスコンストラクターがオブジェクトを部分的または完全に未初期化のままにしてはならないという合意が広まっているようです。
「初期化」とはどういう意味ですか?大まかに言えば、新しく作成されたオブジェクトを、そのクラスのすべての不変条件が保持される状態にするアトミックプロセス。これはオブジェクトに最初に発生するものであり(オブジェクトごとに1回だけ実行する必要があります)、初期化されていないオブジェクトを取得することは許可されません。(したがって、クラスコンストラクターでオブジェクトの初期化を正しく実行するための頻繁なアドバイスです。同じ理由で、
Initialize
メソッドはしばしばアトミック性を分解し、まだ使用されていないオブジェクトを取得して使用することを可能にするため、眉をひそめられます明確な状態にあります。)
問題: CQRSとイベントソーシング(CQRS + ES)を組み合わせると、オブジェクトのすべての状態変化が順序付けられた一連のイベント(イベントストリーム)でキャッチされ、オブジェクトが実際に完全に初期化された状態にいつ到達するのか疑問に思います。クラスコンストラクターの最後、または最初のイベントがオブジェクトに適用された後?
注:「集約ルート」という用語の使用は控えています。必要に応じて、「オブジェクト」を読むたびに置き換えてください。
議論の例:各オブジェクトが何らかの不透明なId
値(GUIDと考える)によって一意に識別されると仮定します。そのオブジェクトの状態変化を表すイベントストリームは、同じId
値によってイベントストアで識別できます(正しいイベントの順序については心配しないでください)。
interface IEventStore
{
IEnumerable<IEvent> GetEventsOfObject(Id objectId);
}
さらに、2つのオブジェクトタイプCustomer
とがあると仮定しますShoppingCart
。焦点を当てましょうShoppingCart
:作成されたとき、ショッピングカートは空であり、正確に1人の顧客に関連付けられている必要があります。最後のビットはクラス不変です:にShoppingCart
関連付けられていないオブジェクトはCustomer
無効な状態です。
従来のOOPでは、コンストラクターでこれをモデル化できます。
partial class ShoppingCart
{
public Id Id { get; private set; }
public Customer Customer { get; private set; }
public ShoppingCart(Id id, Customer customer)
{
this.Id = id;
this.Customer = customer;
}
}
ただし、遅延初期化を行わずにCQRS + ESでこれをモデル化する方法に困っています。この初期化の簡単なビットは事実上状態の変化なので、イベントとしてモデル化する必要はありませんか?
partial class CreatedEmptyShoppingCart
{
public ShoppingCartId { get; private set; }
public CustomerId { get; private set; }
}
// Note: `ShoppingCartId` is not actually required, since that Id must be
// known in advance in order to fetch the event stream from the event store.
これは明らかに、ShoppingCart
オブジェクトのイベントストリームの最初のイベントである必要があり、そのオブジェクトは、イベントが適用された後にのみ初期化されます。
したがって、初期化がイベントストリームの「再生」の一部になる場合(これは、Customer
オブジェクトでもShoppingCart
オブジェクトでも、その他のオブジェクトタイプでも同じように機能する非常に一般的なプロセスです)…
- コンストラクターはパラメーターなしで何もせず、すべての作業をいくつかの
void Apply(CreatedEmptyShoppingCart)
メソッドに任せますか(これは眉をひそめたときとほとんど同じInitialize()
です)? - または、コンストラクターはイベントストリームを受け取って再生する必要があります(これにより、初期化が再びアトミックになりますが、各クラスのコンストラクターに同じ一般的な「再生と適用」ロジック、つまり不要なコードの重複が含まれることを意味します)?
- または、オブジェクトを適切に初期化する従来のOOPコンストラクター(上記を参照)と、最初のイベントを除くすべてのイベントの両方に
void Apply(…)
-iedする必要がありますか?
完全に機能するデモ実装を提供するための回答は期待していません。私の推論は欠陥があるところ、誰かが説明できる場合、私はすでに非常に幸せになるだろう、またはオブジェクトの初期化は本当にいるかどうかである最もCQRS + ESの実装における「痛みのポイント」。
Initialize
メソッド)と同じ場所を占めているように思えます。それは私に質問を導きます、あなたのそのような工場はどのように見えるでしょうか?