web-dev-qa-db-ja.com

ドメインモデルの設計-堅牢な流暢性、カプセル化、および拡張性のベストプラクティス

私のアプリケーションでは、可能な限り保守しやすいアーキテクチャができるように努力していますが、適切なドメインモデルの設計を決定できません。

ユーザーのドメインモデルの例を次に示します。

public class User // Referred to as the domain model
{
    // A model whose properties map exactly to a database table schema 
    private UserDataModel Model { get; set; } // Posts data back to the database

    public string FirstName
    {
        get
        {
            return Model.FirstName;
        }
        set
        {
            // Validation & Observer logic here
            Model.FirstName = value;
        }
    }
    public string LastName
    {
        get
        {
            return Model.LastName;
        }
        set
        {
            // Validation & Observer logic here
            Model.LastName = value;
        }
    }
    public string FullName
    {
        return $”{FirstName} {LastName}”;
    }
    public Company Company // Other domain model with a similar design
    {
        get
        {
            return Model.Company;
        }
        set
        {
            // Validation & Observer logic here
            Model.Company = value;
        }
    }

    // List of other domain models with similar designs
    public IList<Order> Orders { get; set; } 
    // …

    internal User(UserDataModel user)
    {
        Model = user;
    }

    // Domain specific methods here 
    //(e.g. IsAdmin, CalculateBill, SendPasswordResetEmail, CanPlaceOrder, etc.)
}

本質的に、私の懸念は、強力な消費、強力なカプセル化、および高い拡張性/保守性またはスケーラビリティを可能にする、効果的で効率的で保守可能なドメイン設計を実現するためのベストプラクティスとは何かに関するものです。具体的な質問は次のとおりです。

  1. プロパティ

    a)ドメインモデルに、基になるデータモデルと同様のプロパティ、またはドメインモデルのアプリケーションの消費にのみ関連するプロパティ(つまり、FullNameなどのプロパティ、Dirtyなどのブールフラグ(更新が発生))が含まれている場合、プロトタイプ(つまり、クライアント/コンシューマーが作成する新しいダミーモデル))?

    b)基になるデータモデルをアクセスのために公開する必要がありますか、それともメソッドからのみアクセス可能にする必要がありますか?データモデルの一部のみを返すか、オブジェクト全体を返すか?

    c)ドメインモデルにリンクされたオブジェクト(例のCompanyオブジェクトのように)、またはそのリンクされたオブジェクトに関連する値のみ(つまり、オブジェクト全体ではなくCompanyオブジェクトのNameプロパティ)を含める必要があります?

    d)セッターは上記と同様にする必要がありますか、それともドメインモデルで呼び出されるメソッド、またはプロパティセッターで呼び出されるプライベートメソッド(つまり、set {SetFirstName(value);})を介してのみですか? (注:セッターは検証エンジンの呼び出しとオブザーバーの呼び出しとサービスの呼び出しを実装するため、比較的チャンクになります)

  2. 建設

    a)クライアント/コンシューマーからの新しいドメインモデルの構築には、いくつかのsetメソッドを使用するか、クライアント/コンシューマーレベルまたはファクトリー/ビルダーパターンを介して純粋にプロパティをマッピングする必要がありますか?

    b)ドメインモデルは、それが表す基になるデータ構造を完全に非表示にする必要がありますか(つまり、データベースの一部ではないオブジェクトであるかのように表示されます)?

    c)検証は構築レベルで(つまり、プロパティが設定されたときに)行われるべきですか、それともクライアント/コンシューマがドメインモデルで「IsValid」メソッドを呼び出すのに任せるべきですか?

  3. 検証

    a)検証エラーをドメインモデルのコレクションに格納するか、別のエラーモデル/シングルトンクラスに格納する必要がありますか?

    b)ドメインモデルレベルまたはデータモデルレベルで検証を行う必要がありますか?

  4. 検索

    a)オブジェクト(つまり、注文)のリストの場合、リストはプライベートであり、「GetOrder(string orderNo)」メソッドを介してアクセスする必要がありますか?これは単一責任の原則に違反しているのではないでしょうか(つまり、メソッドは疑似リポジトリ/ファクトリです)、いいえですか?

    b)リストに含まれるドメインモデルまたはドメインモデルに影響を与えずに、リスト全体を取得するにはどうすればよいですか?潜在的にメモリを大量に消費する可能性がありますが、クローンを使用する必要がありますか?

私は理想的には、消費またはテストするのが面倒なメンテナンス不可能なドメイン設計の開発を回避しようとしています。ドメインモデルをできるだけ流暢で堅牢なものにして、クライアント/コンシューマーの文章のように読み取って、基になるデータ構造ではなく実際のドメインを表すことができるようにしたい(つまり、それがその上に立っているかのように動作できるようにする必要がある)バックエンドなしで所有します)。

6
G.T.D.

率直に言って、データモデルのフィールドを単に通過するグルークラスは、アプリケーションに大きな価値をもたらしません。また、機能の観点からも特に興味深いものではありません。

したがって、個々の質問を深く掘り下げる前に、簡単な基​​本アーキテクチャを提案します。

DB <---> ORM <---> SL/BLL <---> VM <---> V

DBはデータベースです。 ORMはオブジェクトリレーショナルマッパーです。 SQLを介してデータベースと通信し、CRUDメソッドを公開します。 SL/BLLはサービスレイヤー/ビジネスロジックレイヤーです。 CreateInvoiceなどのビジネスオペレーションをORMで使用するCRUDメソッドに変換します。 VMはViewModelであり、SL/BLLとのUIインタラクションを調整します。Vはビューであり、サーフェスレベルのUIインタラクションと検証コードビハインドが含まれています。

サンプルコードはC#を連想させます。 C#では、ORM(非常に可能性の高いエンティティフレームワーク)が、partialキーワードを使用してドメインロジックに接着できるコード生成エンティティクラスを生成できます。これにより、各エンティティクラスのカスタムドメインと検証ロジックを記述せずにDTOのボイラープレートをすべて作成するのは大変です。

このような無駄のないアーキテクチャーにより、最小限の必要な定型コードで簡単に保守可能なアプリケーションを開発できます。 Java)で精巧なクリスタル大聖堂のアーキテクチャを構築しているのでない限り、この代表的なアーキテクチャは、必要な抽象化のすべてであるべきです。

7
Robert Harvey
  1. a)

ドメインモデルに、基になるデータモデルと同様のプロパティが含まれている場合

いいえ、これはほとんど当てはまりません。そうである必要はありません。オブジェクトにデータを保存する必要はありません。 Smalltalkの背後にいる男性で、現在OOPとして認識しているアランケイ は、彼のオブジェクト がデータを削除することを望んでいました 。彼は、データが配置されているストレージへの参照のみを持つオブジェクトを提案しました。これは、オブジェクトの検索に関する特定の考え方の結果です。それらは、データではなく 望ましい動作 に基づいて識別される必要があります。このアプローチに従って、私はあなたが単純にORMを必要としないという結論に達しました(それは単に恐ろしい概念であることに加えて)。 そうです それなしで生きることができます。

b)理想的には、データはその所有者以外のオブジェクトに決して公開されるべきではありません。行動のみが公開されるべきです。これは基本的なものですOOP encapsulation と呼ばれる原則前のアイテムのジョブ、 ここ は、UIのゲッターを回避する方法です。

c)この項目に回答することは、私がすでに書いたことを考えれば意味がありませんが、考え方が少し変化することは理解しています。そのため、DDDに関してお答えします。ある集約には別の集約の識別子へのリンクを含めることができます。アグリゲートはシステムを配布するのに適した候補シームを表すため、必要に応じてそれらを配布する方が簡単です。

d)アイテムbのアプローチを適用すると、ほとんどのオブジェクトが不変になるため、これも意味がありません。しかし、まだ準備ができていない場合は- セッターを非公開にする必要がありますセッターは悪い ので、セッターはまったくありません。データを変更する必要がある場合は、動作として公開し、項目b)の手法を使用して、オブジェクトの状態を変更する必要はありません。

2。

a)キーワード「new」を使用してオブジェクトを作成する必要があります。これで十分でしょう。 DIコンテナは必要ありません 。パブリックセッターはありません。 ORMを使用する場合は-ok、 data-mapper を使用してデータベース列をオブジェクトプロパティにマップします。

b)例を見る ここ

c) always-valid camp キャンプがddd-communityで普及しています。検証を処理するよりエキゾチックな方法は、 decorators を使用することです。この手法が登場したのは、 David West が使用するレゴレンガの比喩のためです。オブジェクトのライフタイムには基本的に2つの異なる段階があると述べています。オブジェクトの作成と実際の動作です。したがって、オブジェクトの作成を妨げるものはありません。すべての動作、チェックは「作業」フェーズで行う必要があります。しかし、私は繰り返しますが、それは非常にエキゾチックなアプローチです。

  1. 検証についてはすでに何かが伝えられています。私に役立つリンクをリストします: onetwothreefour .

  2. 質問はデータ中心の考え方を意味します。私が提供したリンクを読んだ後、あなたは何をすべきかがわかると思います。

4
Zapadlo