2

ドメイン駆動設計とイベント ソーシングを試しています。それらをバインドするために、NServiceBus、JOliver の EventStore、および NES を使用 (C# で開発) する予定です。単純なケース (値オブジェクトのみを持つ 1 つの集約ルート) で機能するインフラストラクチャが既にあります。

私は Evans のブルーブックを読んでいて、私の仕事の分野 (HVAC メンテナンス会社の ERP と CRM) から例を挙げて、単純なドメイン モデルを開発しようとしています。

私は単純なサブドメイン、つまり HVAC マシンとそれらの間の関係をモデル化しています。機械には、炉、バーナー、エアコン、コンプレッサー、汎用コンポーネントなど、さまざまな種類があります。各マシンは、複数の子マシンを持つことができます。すべてのマシン タイプは、いくつかの共通のデータといくつかの共通の動作を共有します。ただし、各タイプには追加のデータと特定の動作があります。たとえば、Burner オブジェクトは Furnace にのみ追加できます。

私の分析の最初の結果は、特定のマシンへの参照を保持できる必要があるため (たとえば、単一のマシンを含む修理記録の挿入、障害の記録、など)、また、大規模なマシン ツリーでの同時実行の問題を軽減します。

したがって、私の仮説は次のとおりです。

public class Machine : AggregateBase
{
    public DateTime InstallationDate { get; private set; }

    public Guid ManufacturerId { get; private set; }

    public Guid ModelId { get; private set; }
}

public class Furnace : Machine
{
    public List<Burner> burners { get; private set; }

    // other furnace properties

    public void AddBurner(Burner burner)
    {
        // perform validation
        this.Apply<BurnerAdded>(x=> x.burnerAdded = burner);
    }

    public void Handle(BurnerAdded @event)
    {
        this.burners.Add(@event.burnerAdded);
    }
}

public class Burner : Machine
{
    // burner specific properties/methods
}

しかし、私はいくつかの疑問があります:

  1. これは私のドメインを表す正しい方法ですか? クラスの継承が推奨されていないことを読みましたが、これはそれを使用するのに最適なケースのようです (バーナーはマシンであり、ファーネスもそうです)。継承は 1 レベルに制限します。

  2. イベントソーシングでクラス継承を実装することは可能ですか? 特に提案された技術スタック (nServiceBus、EventStore、NES) では?

  3. 子マシン (炉へのバーナーなど) の追加はどのように実行すればよいですか? この操作は次の 2 つに分けられます。

    1. 新しいバーナーをバーナー リポジトリに追加します。
    2. 親炉のバーナー リストにバーナーへの参照を追加しますが、これら 2 つの操作は 2 つの集約ルートをグローバルに変更するため、操作は 2 つの別個のコマンド ハンドラー/トランザクションで実行する必要があります... しかし、2 つ目は最初のものに依存します...これはモデリングエラーの証拠ですか?nServicebusMessages を一緒にバッチ処理して、単一のトランザクションでアクションを実行することもできますが、これは良くないことを読みました...

    子マシンが親を参照するようにすると、親は (検証に必要な) 子マシンのリストを失い、Guid 以外のプロパティのイベント ソーシング リポジトリを照会できません。

ディスカッションへの貢献に感謝します。

4

2 に答える 2

3
  1. 継承は避けたい。代わりに、単一の Machine 集約を作成し、このマシンが参照する記述子オブジェクトを作成します。記述子は、異なるマシン タイプを区別できる値オブジェクトになります。絶対に必要な場合は、このオブジェクトで継承を使用しますが、集約自体には継承を使用しないでください。

  2. ES でクラス継承を使用する機能は、シリアライザーによってのみ制限されます。ES はデータのシリアライズ方法を指示しませんが、Protobuf や Newtonsoft.Json などを使用すると、継承がサポートされます。ただし、JSON の場合、継承を使用すると、JSON 出力に $type 属性が配置されます。

  3. これは、バーナーが炉から独立して存在する必要があるかどうかによって異なります。いいえの場合、それは Furnace 集約の値オブジェクトまたはエンティティ部分であり、Furnace で全体を永続化する必要があります。はいの場合、それは集約である必要があり、最初に作成してからかまどに追加する必要があります。これは、バーナーを作成するコマンドを送信する対応するサガを最初に開始することによって AddBurnerToFurnaceCommand が処理される NSerivceBus サガで実装できます。バーナーが作成されたら、バーナーと炉の間の関連付けを作成します。Furnace は、Guid によって Burner を参照するだけです。ES では、通常、すべてのクエリがプロジェクションを介して処理され、動作のみが ID による正規集計のイベント ストアを呼び出します。

于 2013-02-15T18:15:33.010 に答える
3

#1については、ノーと言うでしょう。例の集約ルートは Furnace (のみ) である必要があります。バーナーは、現在持っているように、Furnace のコレクションとしてモデル化する必要がありますが、集約ルートであってはなりません。バーナーが継承を介して集約ルートになっているという事実を除いて、継承自体は問題ではないと思います(Burner => Machine => AggregateBaseであるため)。

バーナーがファーネスのコンテキストにのみ存在する場合、おそらくバーナー リポジトリは必要ありません。バーナーは常にファーネスに追加します。新しいバーナーの作成自体が興味深いものであり、独自のイベントが必要かどうかはわかりません。その質問に対する答えによって、両方のイベントが必要か、「追加」イベントだけが必要かが決まります。

于 2013-02-15T17:00:04.840 に答える