DDDを数か月間扱った後も、ドメインサービス、ファクトリ、および相互に関連する集約ルートの一般的な目的について、私はまだ混乱しています。彼らが責任において重なるところ。
例:1)佐賀(プロセスマネージャー)で複雑なドメインエンティティを作成し、その後に2)別の場所で処理する必要がある特定のドメインイベントを作成する必要があるのに対し、3)エンティティは境界コンテキストをマークする集約ルートであること他のエンティティに。
集約ルートとファクトリの概念をドメインに導入すると、サービスは不要になったようです。ただし、そうでない場合、サービスは必要なすべてのものを処理でき、知識と依存関係も持っています。
コード例自動車修理店のユビキタス言語に基づいています
public class Car : AggregateRoot {
private readonly IWheelRepository _wheels;
private readonly IMessageBus _messageBus;
public void AddWheel(Wheel wheel) {
_wheels.Add(wheel);
_messageBus.Raise(new WheelAddedEvent());
}
}
public static class CarFactory {
public static Car CreateCar(string model, int amountofWheels);
}
.. or ...
public class Car {
public ICollection<Wheel> Wheels { get; set; }
}
public interface ICarService {
Car CreateCar(args);
void DeleteCar(args);
Car AddWheel(int carId, Wheel wheel);
}
DDDは、一部は貧血ドメインモデルへの反応であり、エンティティは状態のみを持ち、動作は持ちません。
ある意味では、Carのすべての動作を個別のサービスに含めることができるのは事実ですが、なぜそうしたいのですか?これを機能させるには、Carであらゆる種類の状態を公開する必要があります。これは、通常は(Wheelsのように)非公開にしておきたいものです。
そのようにWheelsを公開すると、どのコードでも、通常のビジネスフローの外で、そのコレクションに対してあらゆる種類のファンキーなことを実行できます。集計のポイントは、ビジネスフロートランザクション間でトランザクション的に一貫性のあるものを持つことです。このようなホイールを露出させると、その安全性が完全に損なわれます。
例:あなたのビジネスが4つの車輪を持つ車のみをサポートしたいとします。 Wheelsへのアクセスをカプセル化すると、それを強制できます。そうしないと、ホイールが露出するため、車に100のホイールを追加することは完全に可能です。
通常、サービスはコーディネーターとして使用されます。コマンドハンドラー(CQRSのようなものを想定)は、コマンドオブジェクトをより強く型付けされたパラメーターに変換し、原始的な執着を減らします。次に、これらの「ドメインっぽい」パラメータを使用してサービスを呼び出すことができます。このサービスは、リポジトリーからエンティティーを取得し、それらのビヘイビアーを呼び出します。アーキテクチャに応じて、変更(イベント)を収集してバスなどに渡します。
より高度なシナリオでは、すべてのエンティティを追跡し、すべてのエンティティからの変更を一度に収集できる作業単位を使用します。
ファクトリー(余談)については、別個のファクトリーを作成する代わりに、静的ファクトリーメソッドを集約クラスに追加することを妨げるものは何もありません。
DDDを数か月間扱った後も、ドメインサービス、ファクトリ、および相互に関連する集約ルートの一般的な目的について、私はまだ混乱しています。彼らが責任において重なるところ。
集計ルートは、状態がビジネスの不変条件と一致するようにする責任があります。 CQRS専門用語では、集約ルートにコマンドを発行してモデルを変更します。
ドメインサービスは、アグリゲートのクエリサポートメカニズムです。たとえば、ドメインサービスが計算をサポートしている場合があります。コマンドハンドラーはドメインサービスを集約に渡し、集約(コマンドの処理)は計算の結果を必要とするため、計算への入力として必要な状態の部分をドメインサービスに渡します。ドメインサービスは計算を行って結果を返し、集計は結果をどう処理するかを決定します。
「ファクトリー」にはいくつかの意味があります。ファクトリを使用して集計境界内にあるエンティティを作成する場合、それは単なる実装の詳細です。オブジェクトの構築が複雑な場合は、それを使用する可能性があります。
場合によっては、「リポジトリ」という用語が使用されます。これは通常、それが集約ルートの作成を担当する永続性レイヤーの一部(ドメインモデルの一部ではない)であることを意味します。大まかに言って、コマンドハンドラ(アプリケーションの一部)はコマンドを検証し、次にリポジトリを使用して、コマンドの宛先である集約ルートをロードします。次に、コマンドハンドラーは、集約ルートで指定されたメソッドを呼び出し、コマンドオブジェクトからパラメーターを渡し、場合によっては引数としてドメインサービスにも渡します。
あなたの例では、尋ねる重要な質問は、コマンドを実行するかどうかを決定する責任がどこにあるかです。アプリケーションは、コマンドの形式が正しいことを確認する責任があります(すべてのコマンドデータが存在し、データは検証エラーをスローせずにドメインによって認識される値に変換されたなど)。しかし、「いいえ、今のところホイールを追加することはできません。ビジネスルールではそれを許可していません!」
DDDの世界では、これが疑いなく集約ルートの責任です。したがって、それを集約ルートに含める必要があると判断するロジックがあれば、ICarServiceはなくなります。
(集約がその状態を公開し、carsサービスがビジネスルールをチェックしてオブジェクトの状態を操作する代替実装は、「貧血」集約の例であるアンチパターンと見なされます。集約はコードのにおいです。集約の「ゲッター」は、多くの場合、特にCQRSではコードの匂いです。CQRSでは、クエリをサポートする責任が「どこか別の場所」にあると想定されていますが、読み取りモデルではそうです)。