web-dev-qa-db-ja.com

ドメインオブジェクトとデータモデルの分離

ドメインオブジェクトとデータモデルの分離に関する多くの記事、ブログ、およびSOトピックを調べました。ほとんどすべての回答が述べました:ドメインとデータの永続化には別々のクラスが必要です。 。

なぜそれが良いのかは理解していますが、この問題を解決するための疑似例も見つかりませんでした。私は以下の考えを持っています-たぶん誰もがこの方法で実装する必要があることを知っていますが、誰もそれをしていませんか?

私のソリューションでは、Automapperを使用してこれを実現しています(ドメインからDBモデル、およびドメインからDTOへのマッピング)。ただし、私のアプローチを他のアプローチと比較したいと思いますが、前述したように、良い例は見つかりません。

たぶん、実際に機能しているアプリケーションには存在しない優れた実践についての言葉があるだけで、ドメインクラスだけをフォームで使用されるデータモデルとして使用する方が簡単です。これにより、キャッシングや変更追跡などの多くの利点が得られます。

私は現在、古いデータベースにデータが格納されているアプリケーションで作業しています。このスキームは、大量のデータを吸うので、この場合、DBモデルをドメインモデルとして使用すると、私のドメインも問題になります。

これについてあなたはどう思いますか?

ORMの機能と開発プロセスの高速化>ドメインとデータモデルの分離と開発プロセスの遅延?

4
bielu000

データベースにデータモデルが既にある場合は、C#コードでデータモデルの別のコピーを記述しないでください。ポイントは何でしょうか?

DBのデータモデルからドメインモデルへのマッピングである実際の問題に注意を向けます。

ORMはそれを行います。

したがって、ORMを使用しない唯一の理由は、マッピングを実行するさらに優れた方法があることです。

これは不可能ではありません。 Dapper "micro-ORM" は非常に人気があります。StackOverflowを強化するのに十分であり、多くのユースケースで優れています。

マイクロORM(またはADO、または生のSQL)ルートを使用すると、データベースから取得したデータを保持するためのDTOクラスが作成される可能性があります。ただし、モデルを構築しようとしているわけではありません。 (そして、はい、DTOとの間のマッピングのために、AutoMapperは私が過去数年間働いてきたほとんどの場所に現れました。オートマッパーコードのエラーは追跡するのが本当に苦痛であることがわかりましたが、それはおそらくそれが使いやすく、乱用)。

はい、別のデータモデルですが、すでに1つあります。したがって、マッピングを行うための最も安価で効果的な方法であれば、ORMを使用します。

私はこの記事といくつかのコメントをかなり認識しました: enterprisecraftsmanship having-the-domain-model-separate-from-the-persistence-model

3
Chris F Carroll

私はNuGetパッケージを思い付きました。これは、あなたが言ったことを正確に行うのに役立ちます。私は古典的なDDDアプローチを使用しています:

  • ドメイン-> DataModel = AutoMapper
  • ドメイン-> DTO = AutoMapper
  • DataModel->ドメイン=ファクトリ
  • DTO->ドメイン=工場

一見すると、3つのモデルを用意するのはやり過ぎのように見えますが、ORMで読み取り専用フィールドをプライベートセッターとして設定する必要があるなど、技術的な懸念に煩わされることなくドメインを設計できます。 t読み取り専用フィールドなどを設定します。

リポジトリレイヤーコード全体は次の場所にあります。 https://github.com/DrMueller/MLH.DataAccess 現在、MongoDBを使用していますが、パターンはほとんど同じです。それを使用する方法の例は、ここにあります: https://github.com/DrMueller/MLH.WebApiExtensions

使い方:

ドメインモデル:

public class Individual : AggregateRoot
{
    public Individual(string id, string firstName, string lastName, DateTime birthdate)
        : base(id)
    {
        Guard.ObjectNotNull(() => firstName);
        Guard.ObjectNotNull(() => lastName);

        FirstName = firstName;
        LastName = lastName;
        Birthdate = birthdate;
    }

    public DateTime Birthdate { get; }
    public string FirstName { get; }
    public string LastName { get; }
}

集約ルートのリポジトリを作成します。次に例を示します。

public class IndividualRepository : RepositoryBase<Individual, IndividualDataModel>
{
    public IndividualRepository(IDataModelRepository<IndividualDataModel> dataModelRepository, IDataModelAdapter<IndividualDataModel, Individual> dataModelAdapter) : base(dataModelRepository, dataModelAdapter)
    {
    }
}

「エンティティ」を表すデータモデル:

public class IndividualDataModel : DataModelBase
{
    public DateTime Birthdate { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

そして、双方向でマッピングするDataModelAdapter:

public class IndividualDataModelAdapter : DataModelAdapterBase<IndividualDataModel, Individual>
{
    private readonly IIndividualFactory _individualFactory;

    public IndividualDataModelAdapter(IIndividualFactory individualFactory, IMapper mapper) : base(mapper)
    {
        _individualFactory = individualFactory;
    }

    public override Individual Adapt(IndividualDataModel dataModel)
    {
        return _individualFactory.Create(
            dataModel.FirstName,
            dataModel.LastName,
            dataModel.Birthdate,
            dataModel.Id);
    }
}

これでほぼ完了です。Repositoryの実装はdatamodelとqueryablesにアクセスでき、インターフェースはドメインモデルでのみ機能します。

質問への回答:インフラストラクチャコードは簡単に汎用化できるため、遅延が最小限に抑えられます。あなたがする必要があるのは、マッピングを定義する可能性を保ち、特別なニーズのためにそれを却下することです。

0