次の方法で共有


ドメインモデル層での設計検証

ヒント

このコンテンツは、.NET Docs で入手できる、またはオフラインで読み取ることができる無料のダウンロード可能な PDF として入手できる、コンテナー化された .NET アプリケーションの電子ブックである .NET マイクロサービス アーキテクチャからの抜粋です。

コンテナー化された .NET アプリケーションの .NET マイクロサービス アーキテクチャの電子ブックの表紙サムネイル。

DDD では、検証規則は不変と考えることができます。 集計の主な役割は、その集計内のすべてのエンティティに対して状態の変更全体にインバリアントを適用することです。

ドメイン エンティティは、常に有効なエンティティである必要があります。 常に true である必要があるオブジェクトには、特定の数のインバリアントがあります。 たとえば、注文明細オブジェクトには、常に正の整数である必要がある数量と、商品名と価格が必要です。 したがって、不変条件の適用はドメイン エンティティ (特に集計ルート) の責任であり、エンティティ オブジェクトは有効でないと存在できません。 不変ルールは単純にコントラクトとして表現され、例外または通知は違反時に発生します。

その理由は、オブジェクトが決して存在してはならない状態にあるため、多くのバグが発生するためです。

次に、UserProfileを受け取るSendUserCreationEmailServiceを提案しましょう。このサービス内でNameがnullではないことをどのように理論的に説明できますか? もう一度確認しますか? もっとありそうなのは... あなたがチェックせずに、「うまくいくことを願っている」ことです。つまり、誰かが送信する前にちゃんとした検証をしてくれることを期待しているのです。 もちろん、TDD を使用する場合、最初に記述すべきテストの 1 つは、名前が null の顧客を送信するとエラーが発生するべきということです。 しかし、このようなテストの記述を何度も繰り返し始めると、..."名前を null にすることが決して許可されない場合はどうなりますか?私たちはこれらのテストのすべてを持っていません!"。

ドメイン モデル レイヤーに検証を実装する

検証は通常、ドメイン エンティティ コンストラクターまたはエンティティを更新できるメソッドで実装されます。 データの検証や、検証が失敗した場合の例外の発生など、検証を実装する方法は複数あります。 また、検証に仕様パターンを使用したり、発生するたびに各検証の例外を返す代わりにエラーのコレクションを返す通知パターンなどのより高度なパターンもあります。

条件を検証して例外をスローする

次のコード例は、例外を発生させてドメイン エンティティの検証を行う最も簡単な方法を示しています。 このセクションの最後にある参照テーブルでは、前に説明したパターンに基づいて、より高度な実装へのリンクを確認できます。

public void SetAddress(Address address)
{
    _shippingAddress = address?? throw new ArgumentNullException(nameof(address));
}

より良い例は、内部状態が変化しなかったか、メソッドのすべての変更が発生したことを確認する必要があることを示しています。 たとえば、次の実装では、オブジェクトは無効な状態になります。

public void SetAddress(string line1, string line2,
    string city, string state, int zip)
{
    _shippingAddress.line1 = line1 ?? throw new ...
    _shippingAddress.line2 = line2;
    _shippingAddress.city = city ?? throw new ...
    _shippingAddress.state = (IsValid(state) ? state : throw new …);
}

状態の値が無効な場合は、最初の住所行と市区町村が既に変更されています。 その結果、アドレスが無効になる可能性があります。

同様の方法をエンティティのコンストラクターで使用し、作成後にエンティティが有効であることを確認する例外を発生させることができます。

データ注釈に基づいてモデルで検証属性を使用する

必須属性や MaxLength 属性などのデータ注釈は、「テーブル マッピング」セクションで詳しく説明されているように EF Core データベース フィールドのプロパティを構成するために使用できますが、.NET Framework の EF 4.x 以降の EF Core でのエンティティ検証 ( メソッドも行いません) ではIValidatableObject.Validate

データ注釈と IValidatableObject インターフェイスは、コントローラーのアクションが通常どおり呼び出される前に、モデル バインド中のモデル検証に引き続き使用できますが、そのモデルは ViewModel または DTO であり、ドメイン モデルの問題ではなく MVC または API の問題です。

概念上の違いを明確にしたので、アクションがエンティティ クラス オブジェクト パラメーターを受け取った場合でも、エンティティ クラスのデータ注釈と IValidatableObject を検証に使用できます。これは推奨されません。 その場合、検証は、アクションを呼び出す直前にモデル バインド時に発生し、コントローラーの ModelState.IsValid プロパティを確認して結果を確認できますが、EF 4.x 以降に行われたように、DbContext でエンティティ オブジェクトを永続化する前ではなく、コントローラーで行われます。

DbContext の SaveChanges メソッドをオーバーライドすることで、データ注釈と IValidatableObject.Validate メソッドを使用して、エンティティ クラスにカスタム検証を実装できます。

IValidatableObjectでは、エンティティを検証するためのサンプル実装を確認できます。 このサンプルでは属性ベースの検証は行われませんが、同じオーバーライドでリフレクションを使用して簡単に実装できる必要があります。

ただし、DDD の観点からは、ドメイン モデルは、エンティティの動作メソッドで例外を使用するか、仕様と通知のパターンを実装して検証規則を適用することで、無駄を省いた状態にすることをお勧めします。

UI レイヤー内でモデルの検証を可能にするために、入力を受け入れる ViewModel クラス (ドメイン エンティティではなく) のアプリケーション レイヤーでデータ注釈を使用することは理にかなっています。 ただし、ドメイン モデル内の検証を除外する場合は、これを行うべきではありません。

仕様パターンと通知パターンを実装してエンティティを検証する

最後に、ドメイン モデルで検証を実装するためのより詳細なアプローチは、後で示す追加リソースの一部で説明するように、通知パターンと組み合わせて仕様パターンを実装することです。

コントロール ステートメントを使用して手動で検証する場合や、Notification パターンを使用して検証エラーの一覧をスタックして返す場合など、これらのパターンのいずれかを使用することもできます。

ドメインで遅延検証を使用する

ドメイン内の遅延検証に対処するには、さまざまな方法があります。 書籍『 Domain-Driven デザインの実装』で、ヴォーン・バーノンは検証に関するセクションでこれらについて説明しています。

2 段階認証

また、2 段階認証も検討してください。 コマンドデータ転送オブジェクト (DTO) に対してはフィールドレベルの検証を使用し、エンティティ内ではドメインレベルの検証を行います。 これを行うには、検証エラーに対処しやすくするために、例外の代わりに結果オブジェクトを返します。

たとえば、データ注釈と共にフィールド検証を使用すると、検証定義を複製しません。 ただし、DTO (コマンドや ViewModel など) の場合、実行はサーバー側とクライアント側の両方にすることができます。

その他のリソース