web-dev-qa-db-ja.com

貧血ドメインモデル:長所/短所

貧血ドメインモデルを使用する場合の長所と短所を知りたいのですが(下記のリンクを参照)。

ファウラー記事

75
Steve Horn

長所:

  • あなたはそれがドメインモデルであると主張し、あなたの開発者の友達に自慢し、あなたの履歴書にそれを置くことができます。
  • データベーステーブルから自動的に自動生成するのは簡単です。
  • 驚くほどうまくデータ転送オブジェクトにマッピングされます。

短所:

  • ドメインロジックが別の場所に存在している可能性があります。おそらくclass(s​​tatic)メソッドでいっぱいのクラスにあります。または、GUIコード。または、複数の場所で、すべて競合するロジックを使用します。
  • これはアンチパターンであるため、他の開発者はオブジェクト指向設計の概念を理解しているかどうかを尋ねます。
36
Terry Wilcox

「貧血領域モデル」がアンチパターンであるのに、これを実装するシステムがなぜそれほど多くあるのでしょうか。

いくつかの理由があると思います

1。システムの複雑さ

単純なシステム(インターネットで見つけるほとんどすべての例とサンプルコード)で、実装したい場合:

注文への製品の追加

この関数をOrderに追加しました

public void Order.AddOrderLine(Product product)
{
    OrderLines.Add(new OrderLine(product));
}

素敵でスーパーオブジェクト指向。

ここで、製品が在庫に存在することを検証し、存在しない場合は例外をスローする必要があることを確認する必要があるとしましょう。

私は注文を在庫に依存させたくないので、実際に注文することはできません。今度はサービスに進む必要があります

public void OrderService.AddOrderLine(Order order, Product product)
{
    if (!InventoryService.Has(product)
       throw new AddProductException

    order.AddOrderLine(product);
}

IInventoryServiceをOrder.AddOrderLineに渡すこともできます。これは別のオプションですが、それでもOrderはInventoryServiceに依存します。

Order.AddOrderLineにはまだいくつかの機能がありますが、通常、これはOrderスコープに制限されていますが、私の経験では、Orderスコープからははるかに多くのビジネスロジックがあります。

システムが基本的なCRUD以上の場合、ロジックのほとんどはOrderServiceになり、ごくわずかになります。

2。 OOPの開発者の視点

エンティティにどのロジックを適用すべきかについては、インターネット上で多くの激しい議論があります。

何かのようなもの

Order.Save

注文はそれ自体を保存する方法を知っている必要がありますか?そのためのリポジトリがあるとしましょう。

注文で注文明細を追加できますか?簡単な英語でそれを理解しようとしても、あまり意味がありません。ユーザーが注文に製品を追加するので、User.AddOrderLineToOrder()を実行する必要がありますか?それはやり過ぎのようです。

OrderService.AddOrderLine()はどうでしょう。今、それはちょっと理にかなっています!

OOP=についての私の理解は、カプセル化のために、関数がクラスの内部状態にアクセスする必要があるクラスに関数を配置することです。Order.OrderLinesコレクションにアクセスする必要がある場合は、Order.AddOrderLine()を配置します。この方法では、クラスの内部状態は公開されません。

3。 IoCコンテナ

IoCコンテナを使用するシステムは通常、完全に貧弱です。

これは、インターフェイスを持つサービス/リポジトリをテストできますが、すべてにインターフェイスを配置しない限り、ドメインオブジェクトを(簡単に)テストできないためです。

「IoC」は現在、すべてのプログラミング問題の解決策として高く評価されているため、多くの人が盲目的にそれに従っており、この方法は最終的に貧血ドメインモデルになります。

4。 OOPは難しい、手続きは簡単です

私はこれについて少し " Curse of Knowledge "を持っていますが、DTOとサービスを持つ新しい開発者にとっては、Rich Domainよりもはるかに簡単であることを発見しました。

おそらくそれは、リッチドメインを使用すると、どのクラスにロジックを配置するかを知ることが難しくなるためです。新しいクラスを作成するのはいつですか?使用するパターンは?等..

ステートレスサービスでは、最も近い名前のサービスでそれを平手打ちします。

128
Eric P

これに続いて、長い間頭の中で考えていました。 「OOP」という言葉は、本来意図されていない意味を持っていると私は信じています。アナグラムは、よく知られているように、「オブジェクト指向プログラミング」を意味します。もちろん、焦点は「指向」という言葉にあります。 「OMP」ではなく、「オブジェクト必須プログラミング」を意味します。 ADMとRDMの両方がOOPの例です。オブジェクト、プロパティ、メソッドインターフェイスなどを利用します。ただし、ADMとRDMの違いは、カプセル化する方法にあります。彼らは2つの異なるものです。 ADMが悪いと言うにはOOPは正確なステートメントではありません。カプセル化のさまざまなレベルには別の用語が必要な場合があります。さらに、アンチパターンという用語は好きではありませんでした。通常、 ADMとRDMはどちらも有効なパターンであり、単純なことは異なる目的を念頭に置いており、異なるビジネスニーズを解決することを目的としています。DDDを実践する私たちには、少なくともこれを認める必要があります。そして、ADMを実装することを選択した人々をたたくことによって他の人のレベルに落ちないでください。

20
Drew

「これはアンチパターンなので、他の開発者はオブジェクト指向設計の概念を理解しているかどうか尋ねます。」

「貧血ドメインモデルはアンチパターンです。アンチパターンには長所がありません。」

貧血ドメインモデルがアンチパターンであるかどうかは、意見の問題です。 Martin Fowlerはそうだと言います、OOを知っている多くの開発者は裏返しではないと言っています。事実としての意見はめったに役に立ちません。

たとえそれがアンチパターンであると広く受け入れられていたとしても、その可能性はまだいくらか(比較的少ないものの)アップサイドを持っているでしょう。

15
Matt

Fowlerの主な反対点は、ADMがOOではないということです。他のコードによって操作されるパッシブデータ構造を中心にシステムを「ゼロから」設計する場合、これは確かにオブジェクト指向設計よりも手続き型設計のような匂いがします。

この種のデザインを生み出す力は少なくとも2つあると思います。

  1. newシステムを作成するために、オブジェクト指向の環境で作業する必要がある(またはできると想定している)ことをまだ手続き上必要であると考えるデザイナー/プログラマー、および

  2. (言語に関係なく)非OO方式で設計されたlegacyシステムにサービスのような「顔」を付けるために作業している開発者。

たとえば、既存のCOBOLメインフレームアプリケーションの機能を公開するために一連のサービスを構築している場合、内部COBOLデータをミラーリングする概念モデルの観点からサービスとインターフェイスを定義できますnot構造。ただし、サービスが既存の非表示の実装を使用するために新しいモデルをレガシーデータにマップする場合、新しいモデルはFowlerの記事の意味で「貧弱」である可能性があります。実際の動作のないTransferObjectスタイルの定義と関係のセット。

この種の妥協は、理想的に純粋なOOシステムが既存の非OO環境と相互作用しなければならない境界で非常によくあることです。

13
joel.neely

貧血ドメインモデル(ADM)は、チームがリッチドメインモデル(RDM)を構築できず、長期にわたって維持できない場合に適しています。 RDMで勝つには、システムで使用される主要な抽象化に注意を払う必要があります。どの開発グループでも、そのメンバーの半分以下、おそらく1/10のみが抽象化に対応していることを理解してください。この幹部(おそらく1人の開発者のみ)がグループ全体の活動に対する影響力を維持できなければ、RDMはエントロピーに屈することになります。

そして、エントロピーRDMは、特定の方法で傷つきます。その開発者は過酷な教訓を学びます。彼らは生きる歴史がないので、最初はステークホルダーの期待に応えることができます。しかし、彼らのシステムがより大きくなると、 複雑(複雑ではない) それはもろくなるでしょう。開発者はコードを再利用しようとしますが、開発中に新しいバグやバックトラックを誘発する傾向があります(したがって、見積もりを超過します)。

対照的に、ADM開発者は、新しい機能のために多くのコードを再利用することを期待しないため、期待を低く設定します。時間の経過とともに、多くの不整合が発生するシステムが存在するようになりますが、おそらく予期せず壊れることはありません。市場投入までの時間は、RDMが成功した場合よりも長くなりますが、利害関係者はこの可能性を認識しそうにありません。

7
Ladlestein

「(言語に関係なく)非OO方式で設計されたレガシーシステムにサービスのような「顔」を付けるために取り組んでいる開発者。」

多くのLOBアプリケーションを考えると、これらのレガシーシステムは、多くの場合、同じドメインモデルを使用しません。 Anemic Domain Modelは、サービスクラスのビジネスロジックを使用してこれを解決します。このすべてのインターフェースコードをモデル内に(従来のOOの意味で))置くことができますが、通常はモジュール性を失うことになります。

5
Joe Wood

Anemic Domain Modelの記事を最初に見つけたとき、「神聖なs ***、それが私がすることです。恐怖です!」私はエリック・エヴァンの本への言及を我慢して追跡し、良い例であると考えて、ソースをダウンロードしました。 「Anemic Domain Modelを使用しない」とは、「サービスクラスを使用しない、メディエーターを使用しない、戦略を使用しない」、または「操作されるクラスにロジックを置く」ことを意味しないことがわかります。

DDDの例には、サービスクラス、XyzUpdaters、シングルトン、IoCがあります。

貧血ドメインモデルとは正確に何なのか混乱しています。 「見ればわかる」と期待しています。今のところ、良いデザインの前向きな例に満足しています。

4
jamie

これは、ほとんどのアンチパターンと同じプロです。多くの人々を長い間忙しくさせておくことができます。マネージャーは、より多くの人を管理するほど給与が高くなる傾向があるため、改善しない強い動機があります。

3

予測可能性が向上します。特にプロジェクトに時間と資材が支払われる場合は、マネージャはそのようにします。すべての変更は多くの作業を意味するので、困難な作業は多くの繰り返し作業の背後に隠れることがあります。適切に設計されたDRYシステムでは、常に新しいことをしているため、予測可能性は非常に悪いです。

1

エリックPの回答と上記の他のいくつかの書面に沿って、ADMの主な欠点はOODの損失であり、特にドメインコンセプトのロジックとデータを一緒に保持することで実装の詳細が非表示になることです。 APIは豊富な場合があります。

エリックはさらに、注文にアイテムを追加する前に在庫を確認するなど、そのクラスで動作するロジックに必要な情報がドメインクラスの外部にあることが多いことを指摘しました。ただし、その答えがこの包括的なロジックを保持するサービスレイヤーなのか、それともオブジェクトデザインの一部としてより適切に処理されるのかについては疑問です。 Somebodyは、在庫オブジェクト、製品オブジェクト、および注文オブジェクトについて知っている必要があります。おそらくそれは単にInventoryメンバーやOrderのリストなどを持つOrderSystemオブジェクトです。これはサービスとあまり変わらないように見えますが、概念的にはより一貫していると思います。

または、次のように見てください。内部クレジット残高を持つユーザーを作成できます。User.addItemToOrder(item)が呼び出されるたびに、アイテムの価格が取得され、追加する前にクレジットが確認されます。 OOデザイン。Service.addItemToUserOrder(user、item)に置き換えることで何が失われるのか正確にはわかりませんが、何が得られるのかもわかりません。損失は追加のコード層、さらに不格好な記述スタイル、および基盤となるドメインモデルの強制的な無知。

1
Michael

ADMを備えた「成熟した」システムで作業したことで、少なくとも、この質問にいくつかの事例的なフィードバックを提供できると感じています。

1)カプセル化の欠如

ADMを備えたライブシステムでは、たとえば、 'obj.x = 100; obj.save '、これがビジネスロジックに違反している場合でも。これは、不変条件がオブジェクトでモデル化された場合には発生しない多くのバグにつながります。 ADMにとって最も深刻な否定的であると私が感じるのは、これらのバグの重大度と普及度です。

ここで機能的なソリューションとADMの手続き型ソリューションが大きく異なり、他のユーザーがADMと機能的なソリューションの表面の類似性を引き付けた可能性のある類似性は偶発的なものであることをここで指摘することが重要だと思います。

2)コード膨張

ADMで生成されるコードの量は、OOP/RDMソリューションで作成されるコードの5〜10倍と推定します。これは、おそらく50%がコードの繰り返し、30%がボイラープレートコード、20%がRDMの欠如から生じる問題の解決または解決によるものです。

3)ドメインの問題に対する理解不足

ADMとドメインの問題に対する理解不足は、ある程度密接に関係しています。素朴なソリューションが発生し、既存のDMでサポートすることが困難なため要件が十分に考慮されず、開発時間が長くなり柔軟性に欠けるため、ADMはビジネスイノベーションの大きな障壁になります。

4)メンテナンスの難しさ

概念が単なるコピーアンドペーストの再実装ではない場合、ドメインの概念が表現されるすべての場所で確実に変更されるようにするには、ある程度の厳密さが必要です。これにより、同じバグが調査され、複数回修正されることがよくあります。

5)オンボーディングの難易度の増加

RDMの利点の1つは、ドメインをより早く理解できるようにする概念のまとまりです。 ADMを使用すると、概念が断片化され、明快さを欠くため、新しい開発者が取得することが難しくなります。

また、ADMの運用サポートコストをRDMよりも高くするように誘惑されましたが、これは多くの要因に依存します。

他の人が指摘したように、RDMの利点についてDDD(Greg Evans、Vince Vaughn、Scott Millett)を見てください。

1
Mr Morphe

システムの複雑性と多様性の粒度が大きくなるにつれて、適切に設計されたメッセージパッシングオブジェクトモデルによって提供されるインターフェイスポイントのカプセル化と統合により、広範囲にわたるリファクタリングなしで重要なコードを変更および維持することがはるかに安全になることに注意してください。

ADMによって作成されたサービスレイヤーは確かに実装が容易ですが(比較的考慮がほとんど必要なく、多くの分散インターフェイスポイントがあるため)、ライブで成長しているシステムを変更するとき、問題が発生する可能性があります。

また、すべてのケースでドメインモデルが必要なわけではないことも付け加えます(ADMモデルはもちろんのこと)。場合によっては、データ駆動型であり、アプリケーション全体のロジック/ビジネスルールに依存しない、より手続き的/機能的なタスクスタイルのタスクを使用する方が良い場合があります。

アプリ全体の長所と短所を決定しようとしている場合、1行のコードの記述を開始する前に、まず特定のアプリケーションのそれぞれの外観を設計することが重要だと思います。両方のスタイルでアプリケーションをCRCまたはワイヤーフレーム化したら、前に戻り、どちらがより意味があり、アプリケーションにより適しているかを判断します。

また、どちらが保守しやすくなるかを先に考えてください...

0
Peter M. Elias

マイケルの答えを拡張するために、コードがどこに行くべきかは(かなり)明確であると思いました。注文と在庫の間の相互作用を処理する専用のメディエーターに。

私の視点から、ドメインに関する重要な点は、ドメインがisInThisState()メソッドなどの単純なテスト動作を保持する必要があることです。私の経験では、これらはほとんどの企業のサービスの涙(sic :))全体に散在しています。どちらもコピーして無限に書き直しました。これらはすべて、標準の結合ルールに違反しています。

私の見解では、アプローチはaimである必要がありますDMこれは、ビジネスの振る舞いを可能な限り多く保持し、残りを明確に指定された領域(つまり、サービスで)

0
StripLight

私のチームは個人的にADMを好みます。ドメインの特定の部分を表すビジネスオブジェクトのセットがあります。サービスを使用して、これらのオブジェクトをデータベースに保存します。ビジネスオブジェクトにはメソッドがありますが、これらのメソッドは内部状態のみを操作します。

RDMではなくADMを使用する利点は、オブジェクトをdbに永続化する方法にあります。レガシーコードシステムで作業する開発者は、(新しいシステムの)ビジネスオブジェクトを使用し、現在のデータアクセスレイヤーを引き続き使用して、これらのオブジェクトをdbに永続化できます。 RDMを使用すると、レガシーシステムの開発者はリポジトリオブジェクトをビジネスモデルに挿入する必要があります。これは、現在のデータアクセスレイヤーと一致しません。

0
caa