DDD AggregateRootを見つける
On 2月 1, 2021 by adminみんなのお気に入りのゲームをプレイして、AggregrateRootを見つけましょう。正規のCustomer / Order / OrderLines / Product問題ドメインを使用しましょう。従来、Customer、order、およびproductはARであり、OrderLinesはOrderの下のエンティティです。この背後にある論理は、顧客、注文、および製品を識別する必要があるということですが、OrderLineは注文なしでは存在しません。したがって、問題のドメインでは、顧客は未配信の注文を1つだけ持つことができるというビジネスルールがあります。一度に。
注文は顧客集約ルートの下に移動しますか?そうだと思います。しかしそうすると、顧客ARがかなり大きくなり、後で同時実行の問題が発生する可能性があります。
または、顧客が特定の製品を一生に一度しか注文できないというビジネスルールがある場合はどうなりますか。これは、顧客が注文を所有することを要求するより多くの証拠です。
しかし、それが来ると出荷に関しては、顧客ではなく注文に対してすべてのアクションを実行します。個々の注文を配達済みとしてマークするために顧客全体をロードする必要があるのは、一種の馬鹿げています。
これは私が提案していること:
class Customer { public Guid Id {get;set;} public string Name { get; set; } public Address Address { get; set; } public IEnumerable<Order> Orders { get; set; } public void PlaceOrder(ThingsInTheOrder thingsInTheOrder) { // Make sure there aren"t any pending orders already. // Make sure they aren"t ordering a Widget if they"ve already ordered a Widget in the past. // Create an Order object and add it to the collection. Raise a domain event to trigger emails and other stuff } } class Order { public Guid Id { get; set; } public IEnumerable<OrderLine> OrderLines { get; set; } public ShippingData {get;set;} public void Ship(ShippedByPerson shippedByPerson, string trackingCode) { // Create a new ShippingData object and assign it from the data passed in. // Publish a domain event } }
私の最大の懸念は、並行性の問題と、注文自体に特徴があるという事実です。集計ルートのs。
回答
集計を定義するための基準は何ですか?
大きな青い本の基本に戻りましょう:
集計:データ変更の目的で ユニットとして扱われる関連オブジェクトのクラスター 。外部参照は、ルートとして指定されたAGGREGATEの1つのメンバーに制限されます。一貫性ルールのセットがAGGREGATEの境界内に適用されます。
目標は、不変条件を維持することです。ただし、ローカルID、つまり単独では意味を持たないオブジェクトのIDを適切に管理することも重要です。
Order
およびOrder line
は間違いなくそのようなクラスターに属しています。例:
-
Order
を削除すると、そのすべての行を削除する必要があります。 - 行を削除するには、次の行の番号を付け直す必要がある場合があります
- 新しい行を追加するには、同じ順序の他のすべての行に基づいて行番号を決定する必要があります。
- たとえば通貨などの注文情報を変更すると、広告申込情報の価格の意味に影響を与える可能性があります(または価格を再計算する必要があります)。
ここでは、完全な集計を示します。一貫性のルールと不変条件を確保するために必要です。
いつ停止するか?
ここで、いくつかのビジネスルールについて説明し、それらを確実にするために、顧客を考慮する必要があると主張します。集計の一部として:
ビジネスがあります顧客は一度に1つの未配達の注文しか持てないという規則。
もちろん、そうではありません。意味を見てみましょう:注文は常に顧客を介してアクセスされます。これは現実ですか?労働者が注文を配達するためのボックスに記入するとき、注文にアクセスするには顧客のバーコードと注文バーコードを読む必要がありますか? ?実際、一般に、注文のIDはグローバルであり、顧客のローカルではありません。この相対的な独立性は、注文を集合体の外に置くことを示唆しています。
さらに、これらのビジネスルールはポリシーのように見えます。これらのルールを使用してプロセスを実行することは、会社の任意の決定です。ルールが守られていない場合、上司は不幸かもしれませんが、データは実際には一貫性がありません。さらに、一晩で”顧客ごとに一度に1つの未配信の注文”が”になる可能性があります顧客ごとに10件の未配達注文”、または顧客とは関係なく”、倉庫ごとに100件の未配達注文”。これにより、集計が正当化されなくなる可能性があります。
回答
短いバージョン
DDDの理論的根拠は、ドメインオブジェクトは機能ドメインの要件を満たす必要がある抽象化であるということです。ドメインオブジェクトがこれらの要件を簡単に満たすことができない場合は、間違った抽象化を使用している可能性があります。
名前付け エンティティ名詞を使用するドメインオブジェクトは、それらのオブジェクトが互いに緊密に結合され、肥大化した「神」オブジェクトになる可能性があり、この質問のような問題を引き起こす可能性があります。 「pするのに適切な場所はどこですかut CreateOrderメソッド?」
「適切な」集約ルートを簡単に識別できるようにするために、ドメインオブジェクトが機能的な高レベルのビジネス要件に基づいている別のアプローチを検討してください。システムのユーザーが実行する必要のある機能要件や動作をほのめかす名詞を選択します。
ロングバージョン
DDDは、オブジェクト指向設計へのアプローチであり、ビジネスでドメインオブジェクトのグラフを作成することを目的としています。システムのレイヤー-ドメインオブジェクトは、高レベルのビジネス要件を満たす責任があり、理想的には、基盤となる永続データストアのパフォーマンスや整合性などをデータレイヤーに依存できる必要があります。
別の見方をすると、このリストの箇条書きになります
- 通常、エンティティ名詞はデータ属性を提案します。
- ドメイン名詞は動作を提案する必要があります
- DDDとOOモデリングは、機能要件とコアドメイン/ビジネスロジックに基づく抽象化に関係しています。
- ビジネスロジックレイヤーは、高レベルのドメイン要件を満たす責任があります
DDDに関する一般的な誤解の1つは、ドメインオブジェクトは物理的な現実に基づいている必要があるというものです。世界の「もの」(つまり、あらゆる種類のデータ/プロパティに起因する、現実の世界で指すことができるいくつかの名詞)、しかし、それらの現実の世界のもののデータ/属性は、釘付けしようとするときに必ずしも良い出発点になるとは限りません機能要件を下げます。
もちろん、ビジネスロジックはこのデータを使用する必要がありますが、ドメインオブジェクト自体は、最終的には機能的なドメイン要件と動作を表す抽象化である必要があります。
たとえば; Order
やCustomer
などの名詞は、動作を意味しないため、一般に、ビジネスロジックとドメインオブジェクトを表すのに役に立たない抽象化です。
ビジネスロジックを表すのに役立つ可能性のある抽象化の種類を探すときは、システムが満たすと予想される一般的な要件を考慮してください。
- 営業担当者として、私は新規顧客の注文を作成して、販売する製品の請求書を価格と数量とともに生成できるようにしたい。
- 顧客サービスアドバイザーとして、保留中の注文をキャンセルして、注文は倉庫オペレーターによって履行されません。
- カスタマーサービスアドバイザーとして、注文ラインを返品して、製品を在庫に調整し、支払いが顧客の元の支払いを介して返金されるようにします。方法。
- 倉庫オペレーターとして、保留中の注文のすべての商品と配送情報を表示して、商品を選択して宅配便で発送できるようにします。
- など。
DDDアプローチを使用したドメイン要件のモデル化
上記のリストに基づいて、いくつかの潜在的なドメインオブジェクトを検討します。このような注文システムの場合:
SalesOrderCheckout PendingOrdersStream WarehouseOrderDespatcher OrderRefundProcessor
ドメインオブジェクトとして、これらはさまざまな動作ドメイン要件の所有権を取得する抽象化を表します。実際、彼らの名詞は、彼らが満たす特定の機能要件を強く示唆しています。
(EventMediator
などの追加のインフラストラクチャもそこにある可能性があります。新しい注文がいつ作成されたか、注文がいつ発送されたかなどを知りたいオブザーバーへの通知。
たとえば、SalesOrderCheckout
はおそらく次のことを行う必要があります。顧客、配送、製品に関するデータを処理しますが、注文の配送、保留中の注文の並べ替え、払い戻しの発行の動作とは関係ありません。
SalesOrderCheckout
ドメイン要件を満たすには、顧客があまりにも多くのアイテムを注文するのを防ぐ、検証を実行する、システムの他の部分の通知を上げるなどのビジネスルールを適用することが含まれます-必ずしも依存する必要なしにこれらすべてを行うことができます他のオブジェクトのいずれか。
ドメインオブジェクトを表すためにエンティティ名詞を使用するDDD
その他eは、Order
、Customer
、Product
ドメインオブジェクトとして;これらの問題の中には、質問でほのめかしている問題があります。
- メソッドが
Order
を処理する場合、Customer
とProduct
、それはどのドメインオブジェクトに属しますか? - これら3つのオブジェクトの集約ルートはどこにありますか?
ドメインオブジェクトを表すためにエンティティ名詞を選択すると、いくつかのことが起こる可能性があります:
-
Order
、Customer
とProduct
は「神」オブジェクトに成長するリスクがあります - 単一のすべてを結び付ける神オブジェクト。
- これらのオブジェクトは互いに緊密に結合されるリスクがあります。
this
(またはself
) - 「リークのある」抽象化を開発するリスク-つまりドメインオブジェクトは、カプセル化を弱める数十の
get
/set
メソッドを公開することが期待されています(そうでない場合は、他のプログラマーおそらく後で..) - ドメインオブジェクトがビジネスデータ(UIを介したユーザーデータ入力など)と一時的な状態(ユーザーアクションの「履歴」など)の複雑な組み合わせで肥大化するリスク順序が変更されたとき)。
DDD、OOデザイン、およびプレーンモデル
DDDおよびOOデザインに関する一般的な誤解は、「プレーン」モデルはどういうわけか「悪い」または「アンチパターン」。MartinFowlerは、 Anaemic Domain Model について説明した記事を書きましたが、記事で明らかにしているように、DDD自体はレイヤー間のクリーンな分離のアプローチを「矛盾」させない
「ドメインオブジェクトに動作を配置することも矛盾してはならないことを強調する価値があります」階層化を使用して分離するという堅実なアプローチ永続性やプレゼンテーションの責任などからドメインロジックを評価します。ドメインオブジェクトに含める必要のあるロジックは、検証、計算、ビジネスルールなどのドメインロジックです。」
言い換えると、他のレイヤー間で転送されるビジネスデータを保持するためにプレーンモデルを使用すること(たとえば、ユーザーが新しい注文を作成したいときにユーザーアプリケーションによって渡される注文モデル)は、「アナエミックドメインモデル」と同じではありません。多くの場合、「プレーン」データモデルは、データを追跡し、レイヤー間でデータを転送するための最良の方法です(REST Webサービス、永続ストア、アプリケーションまたはUIなど)。
ビジネスロジックがこれらのモデルのデータであり、ビジネス状態の一部として追跡される場合がありますが、必ずしもこれらのモデルの所有権を取得するわけではありません。
集約ルート
ドメインオブジェクトの例をもう一度見てください。 –SalesOrderCheckout
、PendingOrdersStream
、WarehouseOrderDespatcher
、OrderRefundProcessor
まだ明らかな集約ルートはありませんが、それはありますこれらのドメインオブジェクトには、重複していないように見える非常に別個の責任があるため、実際には重要ではありません。
機能的には、SalesOrderCheckout
がPendingOrdersStream
と話す必要はありません。これは、前者の仕事だからです。データベースに新しい注文が追加されると完了します。一方、PendingOrdersStream
はデータベースから新しい注文を取得できます。これらのオブジェクトは、実際にはそれぞれとやり取りする必要はありません。その他を直接(おそらくイベントメディエーターが2つの間で通知を提供する可能性がありますが、これらのオブジェクト間の結合は非常に緩いと思います)
おそらく、集約ルートは1つ以上のIoCコンテナーを注入するIoCコンテナーになります。これらのドメインオブジェクトをUIコントローラーに組み込み、EventMediator
やRepository
などの他のインフラストラクチャも提供します。あるいは、ビジネス層の上にあるある種の軽量のオーケストレーターサービスになるかもしれません。
アグリゲートルートは、ドメインオブジェクトである必要はありません 。ドメインオブジェクト間の関心の分離を維持するために、アグリゲートが一般的に良いことです。 rootは、ビジネスロジックのない別個のオブジェクトです。
コメント
- あなたの回答は、本からのドメイン駆動設計を備えたMicrosoft固有のテクノロジであるEntityFrameworkの概念を統合しているために反対しました。同じ名前のエリックエバンスによって書かれました。あなたの答えには、DDDの本と直接矛盾するいくつかのステートメントがあり、この質問はEntity Frameworkについては言及していませんが、具体的にはDDDでタグ付けされています。 ‘質問には永続性についてもまったく言及されていないため、’データベーステーブルがどこに関連しているかわかりません。
- @RibaldEddie回答とコメントを確認するために時間を割いていただきありがとうございます。永続データについての言及は、実際には回答に含める必要がないことに同意します’ id = “8dafa54eaa”>
それを削除するために言い換えました。答えの主な焦点は、”エンティティ名詞は、’傾向があるため、あまり良いドメインオブジェクトクラス名ではないことがよくあります。緊密に結合された肥大化した神オブジェクト”になります。うまくいけば、メッセージとWRTの機能要件/動作の理由がより明確になりますか?
回答
問題のドメインには、顧客は一度に1つの未配信の注文しか持てないというビジネスルール。
そのウサギの穴を深く掘り下げる前に、Gregを確認する必要があります。 セットの一貫性に関するYoungの議論、特に:
障害が発生した場合のビジネスへの影響は何ですか?
多くの場合、正しい答えは防止しようとしないことです。間違ったことが起こりますが、問題が発生する可能性がある場合は、代わりに例外レポートを生成します。
ただし、複数の未配信の注文があると仮定します。あなたのビジネスに対する重大な責任….
はい、未配達の注文が1つだけであることを確認したい場合は、顧客のすべての注文を表示できる集計が必要です。 。
その集計は必ずしも顧客の集計ではありません。
注文キューや注文履歴のようなもので、特定の注文のすべてが顧客は同じキューに入ります。あなたが言ったことから、それは顧客のプロファイルデータのすべてを必要としないので、それはこの集合体の一部であるべきではありません。
ただし、配送に関しては、顧客ではなく注文に対してすべてのアクションを実行します。
はい、実際にフルフィルメントを使用している場合はプルシートの場合、履歴ビューは特に関連性がありません。
履歴ビューは、不変条件を適用するために、注文IDとその現在の処理ステータスのみを必要とします。これは必ずしも注文と同じ集計の一部である必要はありません。集計境界は変更の管理に関するものであり、ビューの構造化ではないことを忘れないでください。
したがって、注文を集計として処理する可能性があります。 、および注文履歴を個別の集計として、2つの間のアクティビティを調整します。
回答
設定しましたストローマンの例。単純すぎて、実際のシステムを反映しているとは思えません。そのため、指定した方法でエンティティとそれに関連する動作をモデル化することはしません。
クラスはモデル化する必要があります。複数の集計に反映される方法での注文の状態。たとえば、顧客がシステムを顧客の注文要求を処理する必要がある状態にした場合、CustomerOrderRequest
またはまたは単にCustomerOrder
、またはビジネスが使用する任意の言語であり、CustomerとOrderLinesの両方へのポインターを保持し、canCustomerCompleteOrder()
これはサービスレイヤーから呼び出されます。
このドメインオブジェクトには、注文が有効かどうかを判断するためのビジネスロジックが含まれます。
注文が有効で処理された場合、このオブジェクトを処理された注文を表す別のオブジェクトに移行する方法があります。
理解の問題は、不自然なものを使用していることだと思います。集計の過度に単純化された例。PendingOrder
は、UndeliveredOrder
とは別の独自の集計であり、
またはCancelledOrder
など。
コメント
- あなたの試みはジェンダーニュートラルな言葉がおもしろいので、カラスを追い払うために女性が畑に立つことは決してないことに注意します。
- @RobertHarveyその’焦点を当てるのは奇妙なことです私の投稿で。かかしと彫像はどちらも、歴史を通じて定期的に女性の形で登場しています。
- ‘しなかった場合は、投稿で区別をしなかったでしょう。’それは重要だとは思わない。言語学の問題として、この用語は”ストローマンです; “性差別に関する予約はほぼ確実に”彼は一体何を話しているのか”あなた自身の用語を発明することによって作成された要因。
- @RobertHarvey誰かがどんなストローマンを知っているか男は、’その用語を聞いたことがなければ、ストローマンが何を意味するのか理解できると確信しています。’私の投稿の内容に焦点を当ててソフトウェアを作成できますか?
回答
VaughnVernonは彼の中でこれについて言及しています第7章(サービス)の冒頭にある「ドメイン駆動設計の実装」の本:
「ドメインモデルでサービスを作成する必要があることを示す最良の兆候は、実行する必要のある操作が気になるときです。集合体または値オブジェクトのメソッドとしての場所の位置」。
したがって、この場合、Customerインスタンスと注文のアイテムのリストを取得する「CreateOrderService」というドメインサービスが存在する可能性があります。
class CreateOrderService { public Order CreateOrder(Customer customer, ThingsInTheOrder thingsInTheOrder) { // Get all the orders for the customer // Check if any of the things to be ordered exist in previous orders // If none have been previously ordered, create the order and return it // Otherwise return null } }
コメント
- ドメインサービスが質問の同時実行性の懸念に対処するのにどのように役立つかについて詳しく説明していただけますか?
コメントを残す