ドメインオブジェクトのリポジトリを使用するか、ドメインオブジェクトをサービスレイヤーにプッシュする必要がありますか?


10

私はトランザクションスクリプトの世界から来たので、DDDを検討し始めたところです。DDD設計をデータベースの永続性と統合する正しい方法がわかりません。これは私が持っているものです:

組織ドメインオブジェクトのインスタンスを取得および保存するためのメソッドがインターフェイスに含まれている、OrganizationServiceと呼ばれるサービスクラス。組織は集約ルートであり、それに関連する他のデータがあります。メンバーとライセンスです。EF6データベースの最初のDBContextは、OrganizationService内で使用され、OrganizationDBエンティティと関連するMemberDBおよびLicenseDBエンティティを取得します。これらはすべて、OrganizationServiceによって取得され、Organizationドメインオブジェクトに読み込まれると、同等のドメインオブジェクトクラスに変換されます。このオブジェクトは次のようになります。

public class Organisation
{
    public IList<Member> Members { get; set; }
    public IList<License> Licenses { get; set; }
}

私はOrganizationServiceでRepositoryパターンを使用していません... EF6は現在、リポジトリを冗長化しているようで、EF自体をリポジトリとして使用しています。

設計のこの時点では、組織ドメインオブジェクトは貧弱です。EFPOCO組織クラスのように見えます。OrganisationServiceクラスは、リポジトリのように見えます!

次に、ロジックの追加を開始する必要があります。このロジックには、組織のライセンスとメンバーの管理が含まれます。トランザクションスクリプトの時代に、これらの操作を処理するためにOrganisationServiceにメソッドを追加し、DBと対話するためにリポジトリを呼び出しますが、DDDでは、このロジックは組織ドメインオブジェクト自体にカプセル化する必要があると思います...

ここで何をすべきかわかりません。ロジックの一部としてこのデータをデータベースに永続化する必要があります。これは、組織ドメインオブジェクト内でDbContextを使用してこれを行う必要があることを意味しますか?ドメインオブジェクト内でリポジトリ/ EFを使用することは悪い習慣ですか?もしそうなら、この永続性はどこに属していますか?

public class Organisation
{
    public IList<Member> Members { get; set; }
    public IList<License> Licenses { get; set; }

    public void AddLicensesToOrganisation(IList<License> licensesToAdd)
    {
        // Do validation/other stuff/

        // And now Save to the DB???
        using(var context = new EFContext())
        {
            context.Licenses.Add(...
        }

        // Add to the Licenses collection in Memory
        Licenses.AddRange(licensesToAdd);
    }
}

代わりに、メモリ内の組織ドメインオブジェクトを変更し、永続化するためにそれをOrganisationServiceにプッシュして戻す必要がありますか?次に、オブジェクトで実際に何が変更されたかを追跡する必要があります(これはEFが独自のPOCOに対して行うことです!EFは単なるリポジトリーの置き換えではなく、ドメインレイヤーにもなる可能性があると感じ始めています)。

ここで任意のガイダンスをいただければ幸いです。

回答:


2

あなたはどちらのアプローチを取り、それをうまく機能させることができます-もちろん、長所と短所があります。

Entity Frameworkは、間違いなくドメインエンティティを充実させること目的としています。ドメインエンティティとデータエンティティが同じクラスである場合にうまく機能します。EFを使用して変更を追跡context.SaveChanges()し、トランザクションの作業が終了したときに電話をかけることができれば、はるかに便利です。また、検証属性を2回設定する必要がないことも意味します。1回はドメインモデルで、もう1回は永続化エンティティで設定します。たとえば、ビジネスロジックで確認したり、ビジネスロジックで確認[Required]たり[StringLength(x)]できるため、試行する前に無効なデータ状態をキャッチできます。 DBトランザクションを実行し、EntityValidationException。最後に、コーディングは迅速です。マッピングレイヤーやリポジトリを記述する必要はありませんが、代わりにEFコンテキストを直接操作できます。これはすでにリポジトリーであり、作業単位であるため、追加の抽象化レイヤーでは何も実行されません。

ドメインと永続化されたエンティティを組み合わせる欠点は[NotMapped]、プロパティ全体に多数の属性が散在することです。多くの場合、永続化されたデータの取得専用フィルターであるか、または後でビジネスロジックで設定され、データベースに永続化されないドメイン固有のプロパティが必要になります。場合によっては、データベースではうまく機能しない方法でドメインモデルにデータを表現する必要があります。たとえば、列挙型を使用する場合、Entityはこれらをint列にマップしますが、おそらくマップする必要があります人間が読める形式stringに変換するため、データベースを調べるときにルックアップを調べる必要はありません。次にstring、マッピングされたプロパティ、enumないプロパティ(ただし、文字列を取得して列挙型にマップします)、および両方を公開するAPI!同様に、コンテキスト全体で複雑な型(テーブル)を組み合わせる場合は、mappedOtherTableIdとマップされていないComplexTypeプロパティを使用することができます。どちらもクラスでパブリックとして公開されます。これは、プロジェクトに精通しておらず、ドメインモデルを不必要に膨らませている人にとって混乱を招く可能性があります。

ビジネスロジック/ドメインが複雑になるほど、ドメインと永続化されたエンティティの組み合わせが制限的または扱いにくくなります。期限の短いプロジェクトや、複雑なビジネスレイヤーを表現しないプロジェクトの場合、EFエンティティを両方の目的に使用することが適切であり、永続性からドメインを抽象化する必要がないと思います。拡張の容易さを最大限に必要とするプロジェクト、または非常に複雑なロジックを表現する必要があるプロジェクトの場合は、2つを分離して、永続化の複雑さをさらに処理する方がよいと思います。

エンティティの変更を手動で追跡する問題を回避する1つのトリックは、対応する永続化されたエンティティIDをドメインモデルに格納することです。これは、マッピングレイヤーによって自動的に入力できます。次に、EFへの変更を永続化する必要がある場合は、マッピングを行う前に、関連する永続エンティティを取得します。その後、変更をマップすると、EFが自動的に変更を検出し、手動で変更context.SaveChanges()を追跡することなく電話をかけることができます。

public class OrganisationService
{
    public void PersistLicenses(IList<DomainLicenses> licenses) 
    {
        using (var context = new EFContext()) 
        {
            foreach (DomainLicense license in licenses) 
            {
                var persistedLicense = context.Licenses.Find(pl => pl.Id == license.PersistedId);
                MappingService.Map(persistedLicense, license); //Right-left mapping
                context.Update(persistedLicense);
            }
            context.SaveChanges();
        }
    }
}

さまざまなアプローチをさまざまな複雑なデータと組み合わせるときに問題はありますか?DBにうまく対応できるものがあれば、EFを直接使用します。ないものがある場合は、EFを使用する前にマッピング/抽象化を導入してください。
陶酔感2014

@Euphoricドメインとデータベースの違いはマッピングで処理できます。ドメインを2回(非永続的と永続的)追加し、変更追跡を手動で処理するというユースケースは、特別な場合にマッピングを追加するよりも簡単ではありません。一つ教えてもらえますか?(NHibernateの使用を想定していますが、EFの方が制限があるのでしょうか?)
Firo

非常に役立つ、実用的で有益な回答。答えとしてマーク。ありがとう!
MrLane 2014

3

私はEF6を知りませんが、他のORMはこれを透過的に処理するため、ライセンスを明示的にコンテキストに追加する必要はありません。変更を保存するときにライセンスを検出します。したがって、メソッドは以下になります

public void AddLicenses(IEnumerable<License> licensesToAdd)
{
    // Do validation/other stuff/
    // Add to the Licenses collection in Memory
    Licenses.AddRange(licensesToAdd);
}

そしてその周りのコード

var someOrg = Load(orgId);

someOrg.AddLicenses(someLicences);

SaveChanges();

私のドメインオブジェクトはエンティティではないので、それらをLicensesコレクションに追加しても、それは単なるリストであり、削除されません。問題はEFに関するものではなく、実際の永続化が行われる場所です。EFでのすべての永続化は、コンテキストスコープ内で行われる必要があります。問題はこれがどこに属しているかです。
MrLane 14

2
ドメインエンティティと永続化エンティティは同じにすることができますが、そうでない場合は、99%の時間で値が追加されず、変更の追跡が失われる抽象化の別のレイヤーを導入します。
フィロ2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.