ドメインエンティティは単一責任の原則に違反していますか?


13

エンティティの単一の責任(変更する理由)は、それ自体を一意に識別することである必要があります。言い換えると、その責任を見つけられるようにすることです。

エリックエヴァンのDDDの本、ページ。93:

エンティティの最も基本的な責任は、動作が明確で予測可能なように継続性を確立することです。彼らは予備にされている場合、彼らはこれを最善にします。属性や動作に焦点を当てるのではなく、Entityオブジェクトの定義を最も本質的な特性、特にそれを識別する、またはそれを検索または一致させるために一般的に使用される特性まで取り除きます。その動作に必要な概念と属性に不可欠な動作のみを追加します。

それを超えて、コアエンティティに関連付けられた他のオブジェクトへの動作と属性を削除することを検討してください。

1。

... ENTITYオブジェクトの定義を最も本質的な特性、特にそれを識別する、またはそれを検索または一致させるために一般的に使用される特性まで取り除きます。コンセプトに不可欠な動作のみを追加します...

いったんエンティティが割り当てられている固有のIDを、そのアイデンティティが確立され、私はそのような実体がするいかなる行動を必要としないと仮定しますので、そのアイデンティティを維持したりするために、それは自分自身を識別するのに役立ちます。したがって、著者が「概念に不可欠な振る舞いfindとはどのような振る舞いを参照しているのか理解できません(およびmatch 操作以外)。

2。

... ENTITYオブジェクトの定義を最も本質的な特性、特にそれを識別する、またはそれを検索または一致させるために一般的に使用される特性まで取り除きます。...さらに、動作と属性を削除して、コアENTITYに関連付けられている他のオブジェクトを探します。

そのため、エンティティを特定するのに役立たないが、そのエンティティの固有の特性として行動を特徴付けます(つまり、characterえは犬に固有、飛行は飛行機に固有、産卵は鳥に固有です。) 。)、そのエンティティに関連付けられている他のオブジェクトに配置する必要があります(例:犬のエンティティに関連付けられているオブジェクトにbarえる動作を配置する必要があります)?

3。

それを超えて、コアENTITYに関連付けられた他のオブジェクトの動作と属性を削除してください。

a)MyEntity責任A_respをおよびにそれぞれ委任B_respします。ab

ほとんどのにもかかわらずA_respB_resp仕事をすることによって行われa、およびbインスタンス、クライアントはまだ提供されているA_respB_resp通じMyEntityクライアントの観点から二つの責任はに属していることを意味しています、MyEntity。したがって、それはMyEntityまたA_respB_resp責任と責任を持っていることを意味していないので、SRPに違反していますか?

B)私たちがいることを前提としていてもA_respB_respに属していないMyEntityMyEntityまだ責任があるAB_respオブジェクトの動作を調整するにaしてb。そうしないMyEntity違反SRPを最低でも、それは持っているので、2つの責任を一意に自身を識別し、またして- AB_resp

class MyEntity
{
    private A a = ...
    private B b = ...


    public A GetA()
    { ... }

    public B GetB()
    { ... }

    /* coordinates operations of objects a and b */
    public int AworkB()
    { ... }
}

/* A encapsulates a single responsibility resp_A*/
/* A is value object */
class A
{ ... }

/* B encapsulates a single responsibility resp_B*/
/* B is value object */
class B
{ ... }

更新:

1。

このコンテキストでの動作は、セマンティック動作を指します。たとえば、クラスのプロパティ(つまり、ドメインオブジェクトの属性)を一意に識別するために使用されるプロパティには、動作があります。これはコードでは直接表されませんが。予想される動作では、そのプロパティに重複する値はありません。

そのため、コードでは、エンティティのIDを何らかの形で維持する動作(つまり、操作)を実際に実装する必要はほとんどありません。そのような動作は、ドメインモデルの概念として(IDエンティティ)、ただし、このID属性をコードに変換すると、そのセマンティクスの一部が失われます(つまり、ID値が一意であることを暗黙的に確認する部分が失われます)?

2。

さらに、AgeなどのプロパティはPersonエンティティの外部にコンテキストを持たないため、別のオブジェクトに移動しても意味がありません...しかし、その情報は一意の識別子、つまり動作への混乱した参照。年齢は、遅延読み込み値である可能性があります。

a)Ageプロパティが遅延ロードされる場合、意味論的Ageに単なる属性であるにもかかわらず、ビヘイビアと呼ぶことができますか?

3。

有効な住所であることの確認など、住所固有の操作を簡単に行うことができます。設計時にはそれを知らないかもしれませんが、このコンセプト全体はオブジェクトを最小の部分に分解することです

Age別のオブジェクトに移動するとコンテキストが失われることに同意しますが、DateOfBirthプロパティを別のオブジェクトに移動してもコンテキストは失われませんが、通常は移動しません。

Address別のオブジェクトに移動する主な理由は何DateOfBirthですか?エンティティにDateOfBirth本質的であるため、Personまたは将来どこかに特定の操作を定義する必要がある可能性が低いためDateOfBirthです。

4.私は、私はまだかどうかわからないと言わなければならないMyEntityもありA_respB_resp責任と理由MyEntityもあるが、AB_resp違反とはみなされませんSRP

EULERFX

1)

著者が参照している動作は、エンティティに関連付けられている動作です。これらは、エンティティの状態を変更する動作です

a)私があなたを正しく理解しているなら、あなたは、エンティティがその属性(すなわち、その状態)を変更する振る舞いのみを含むべきだと言っているのですか?

b)は約何の行動は必ずしも変更しないエンティティの状態を、まだあると考えている固有のものの特性エンティティ(例:吠えるでしょう固有の特性Dog、それは変更しなかった場合でも、エンティティ犬の状態)?これらの動作をエンティティに含める必要がありますか、それとも他のオブジェクトに移動する必要がありますか?

2)

動作を他のオブジェクトに移動する限り、著者は特に値オブジェクトを参照しています。

私の引用には含まれていませんが、著者は同じ段落で、場合によっては行動(および属性)も他のエンティティに移動することを言及しています行動をVO に移動する利点は理解していますが)

3)と仮定すると、MyEntity(質問参照3.私たちはと言うでしょう、SRPを違反しない、私のオリジナルのポストに)責任のはMyEntityまたで構成される他のものの間です。

a。A_resp + B_resp + AB_respAB_respオブジェクトaを調整し、b

または

b。 AB_resp +委任A_respおよびB_respオブジェクトに(aおよびb)に関連付けられていますかMyEntity

4)Eric EvanのDDDの本、ページ。94:

CustomerIDは顧客ENTITYの唯一の識別子です(図5.5)が、電話番号と住所は顧客の検索または照合によく使用されます。名前は人の身元を定義するものではありませんが、多くの場合、それを決定する手段の一部として使用されます。

この例では、電話と住所の属性が顧客に移動しましたが、実際のプロジェクトでは、その選択はドメインの顧客が通常どのように一致または区別されるかに依存します。たとえば、顧客がさまざまな目的で多くの連絡先電話番号を持っている場合、その電話番号はIDに関連付けられておらず、営業連絡先に残る必要があります。

a)

CustomerIDは顧客ENTITYの唯一の識別子です(図5.5)が、電話番号と住所は顧客の検索または照合によく使用されます。名前は人の身元を定義するものではありませんが、多くの場合、それを決定する手段の一部として使用されます。

引用はアイデンティティに関連している属性だけが実体に残るべきであると述べます。著者は、エンティティがこのエンティティの検索または照合によく使用される属性のみを含むべきであり、他のすべての属性は移動する必要があることを意味すると思いますか?

b)しかし、他の属性はどのように/どこに移動する必要がありますか?例えば、(ここでは仮定は、あるアドレス属性をするために使用されていない見つけたり一致し Customer、したがって、我々は、移動したいアドレス属性を外にCustomer):

代わりに有するのであればCustomer.Address(タイプのstring)私たちは財産作成Customer.AddressタイプのをAddress、私たちは移動しなかったアドレス属性を(型である関連したVOオブジェクトにAddress)か、我々はそれを言うだろうCustomer、まだ含まれているアドレス属性を

c)

この例では、電話と住所の属性が顧客に移動しましたが、実際のプロジェクトでは、その選択はドメインの顧客が通常どのように一致または区別されるかに依存します。たとえば、顧客がさまざまな目的で多くの連絡先電話番号を持っている場合、その電話番号はIDに関連付けられておらず、営業連絡先に残る必要があります。

私たちは、多くの各想定した場合以来、ここで間違っにおける作者ではありません連絡先の電話番号Customerのみ、その特定に属しているがCustomer、その後、私はこれら言うと思います電話番号が関連付けられているアイデンティティちょうどその時限りCustomerだけ持っていた1つの電話番号を

5)

著者がエンティティを削除することを提案する理由は、最初に顧客エンティティを作成するときに、顧客に関連付けられていると考えることができる任意の属性をエンティティに入力する傾向があるためです。これは、最終的に貧弱なドメインモデルにつながる動作を見落とすデータ中心のアプローチです。

オフトピックが、私は思った貧血のドメインモデルの移動からの結果を行動を外のエンティティあなたの例では、投入されている間、実体のたくさんの属性につながる、Customerあまりにも多くの持つ行動を、我々はおそらくに含めたいので、(行動ましたこれらの追加属性を変更してください)、したがってSRPに違反していますか?Customer

ありがとう


2
robert martins clean code video series、cleancoders.comを強くお勧めします。彼は、さまざまな原則がどのように問題を引き起こすか、または互いにバランスを取ることができるかについて、非常に詳細に説明します。そうでなければ、あなたの例の式の一部は、Personオブジェクトが関係している期間を見ていると思います。購入のように短時間の場合、購入に使用される請求先住所はその一部であり、変更できません。ライブラリアカウントの場合、アドレスは変更できるはずです。
カルタロット

2
この質問はSRPに違反していると思われます...;)
IntelliData

回答:


6

このコンテキストでの動作は、セマンティック動作を指します。たとえば、クラスのプロパティ(つまり、ドメインオブジェクトの属性)を一意に識別するために使用されるプロパティには、動作があります。これはコードでは直接表されませんが。期待される動作は、そのプロパティに重複する値がないことです。アドレスなど 、独自のIDを持ちますが、Person Entityのコンテキスト外に存在しないものは、それ自体のオブジェクトに移動する必要があります。したがって、エンティティを集約ルートにプロモートします。

さらに、AgeなどのプロパティはPersonエンティティの外部にコンテキストを持たないため、別のオブジェクトに移動する意味がありません。コンテキストは失われるため、Personエンティティに不可欠な値であることを安全に判断できます。そうでなければ、値を見つけることができませんでした。ただし、その情報は一意の識別子とは別の場所に簡単に格納できるため、動作に対する混乱した参照になります。年齢は、遅延読み込み値である可能性があります。

あなたの質問に答えるために。いいえ、単一責任原則に違反しません。Personは個人のものだけを持ち、Addressのような、より複雑で個人に関連するものは、それ自身のエンティティとして存在するべきではないということを簡潔に述べています。

有効な住所であることの確認など、住所固有の操作を簡単に行うことができます。設計時にはそれを知らないかもしれませんが、この概念全体はオブジェクトを最小の部分に分解することであり、このようなことは事後に行われると比較的簡単になります。

更新:1)ほとんどの場合、このID検証は、オブジェクトをデータストアに保存するときに行われます。つまり、エンティティ検証を表すコードは存在しますが、他の場所に存在します。通常、ID値の発行を担当するコードと共に存在します。そのため、一意性はエンティティのコードで直接表されないと述べています。

2)正しいステートメントAgeは、振る舞いを持つ属性です。そのプロパティを使用する開発者がそのプロパティを使用する方法について正確な決定を下せるように、Ageは遅延ロードされるという事実を文書化する必要があります。

3)DateOfBirth通常、別のオブジェクトです。事前に定義済みの操作が既に行われている日付オブジェクト。一部の言語では、日付オブジェクトにはすでにドメインモデル全体が定義されています。たとえば、c#ではタイムゾーンを指定できます。日付がUTCの場合、日付を加算および減算してタイムスパンを取得できます。したがって、移動についてのあなたの仮定DateOfBirthは正しいでしょう。

4)MyEntity行うのが委任と調整のみである場合、SRPに違反しません。その唯一の責任は委任と調整であり、ファサードパターンと呼ばれます。


私が行った更新をご覧いただけますか?
EdvRusj

回答を更新しました
チャールズランバート

4

とても良い質問です。SRPはそれほど文字通りに解釈されるべきではありません。識別/ルックアップは、SRPに関するエンティティの責任ではありません。他の誰かがそれにID(つまりストア)を与え、それを検索(つまりリポジトリ)する責任があります

エンティティの主な目的は、モデルによって明らかにされた概念を表すことです。エンティティと値オブジェクトの唯一の違いは、エンティティが非識別属性を超えて意味を持つことです。たとえば、Personが名前を変更した場合でも、名前は異なるだけで同じ人物です。


1

エンティティに一意のIDが割り当てられると、そのIDが確立されるため、そのようなエンティティは、IDを維持したり、自身を識別するのに役立つ動作を必要としないと思います。したがって、著者が「検索と一致操作以外に」「コンセプトに不可欠な動作」と呼んでいるのはどのような動作なのか理解できませんか?

アイデンティティが確立されている場合、はい、エンティティは他に何も識別する必要はありません。著者が参照している動作は、エンティティに関連付けられている動作です。これらは、エンティティの状態を変更する動作です。たとえば、CustomerエンティティにMakePreferred動作がある場合があります。著者がエンティティを削除することを提案する理由は、最初にエンティティを作成するときにCustomerエンティティをに、顧客に関連付けられていると考えることができる属性をエンティティに入力する傾向があるためです。これは、最終的に貧弱なドメインモデルにつながる動作を見落とすデータ中心のアプローチです。

動作を他のオブジェクトに移動する限り、著者は特に値オブジェクトを参照しています。VOをVOに移動するのが良い理由は、VOは通常エンティティよりも「小さく」、それにより集中しているためです。さらに、不変性や操作の閉鎖などの側面により、コードに関する推論が簡素化されると同時に、コードがより強固になります。ます。

VOとともに、エンティティは、その動作を実装するさまざまなVOを調整する一種のアンカーとして機能します。

SRPに関しては、混乱は保証されません。エンティティのステレオタイプのOOP実装の1つの問題は、アイデンティティと状態の混同です。実際、行動の観点から見ると、アイデンティティは行動とは何の関係もありません。つまり、エンティティのアイデンティティは、その動作のいずれにも必要ありません。AggregateSourceここで説明する機能的なアプローチなど、この混同が解消される実装があります

もう1つの問題は、SRPがある程度まで定性的な尺度になることです。誰でも、一部のクラスが違反する単一の責任の定義を思い付くことができます。エンティティの責任は、そのエンティティに必要な動作を実装することであると言えます。その意味で、それは単一の責任を持っています。さらに、エンティティが構成要素VOに動作を委任する場合、SRPに違反していません。SRPは、この種の構成を禁止していません。オブジェクト間の結合を最小限に抑えたり、インターフェイスをできるだけむき出しにするなどの注意が必要です。

更新

a)私があなたを正しく理解しているなら、あなたは、エンティティがその属性(すなわち、その状態)を変更する振る舞いのみを含むべきだと言っているのですか?

はい、例外はありますが...

b)そして、エンティティの状態を必ずしも変更するわけではないが、そのエンティティの固有の特性であると見なされている動作についてはどうでしょうか(例:barえは、ドッグエンティティの固有の特性です。犬の状態を変更します)?これらの動作をエンティティに含める必要がありますか、それとも他のオブジェクトに移動する必要がありますか?

エンティティのインスタンスを作成するためのファクトリメソッドをエンティティに含めることは事実上子エンティティですが、オブジェクト参照はトラバーサルに使用されません。この場合、子エンティティはアプリケーションサービスによって永続化される必要があります。アプリケーションサービスは、親エンティティを使用して子エンティティを構築します。

3)MyEntity(私の元の投稿の質問3。

実装の観点から責任を見ています。代わりに、エンティティを、責任を持つ一種のブラックボックスと見なします。それをどのように処理するかは、外部から見ている誰かにとっては興味がありません。VOまたは他のエンティティ間での責任の分割は、実装の懸念事項です。

引用では、IDに関連付けられた属性のみがエンティティに残るべきであると述べています。著者は、エンティティがこのエンティティの検索または照合によく使用される属性のみを含むべきであり、他のすべての属性は移動する必要があることを意味すると思いますか?

より具体的には、振る舞いや検索に必要でない属性は、エンティティの一部であってはなりません。なぜわざわざ?さらに、read-model patternのようなものを使用すると、エンティティは動作に必要な属性以外に何も必要としません。

Customer.Address(type string)の代わりに、Address型のCustomer.Addressプロパティを作成した場合、address属性を関連付けられたVOオブジェクト(Address型)に移動しましたか、それともCustomerにまだ住所があると言いますか属性?

はい、事実上、文字列アドレスとAddress VOアドレスに違いはありません。

顧客がその特定の顧客にのみ属している多くの連絡先電話番号のそれぞれを仮定すると、これらの電話番号は顧客が持っていたときと同じようにアイデンティティに関連付けられていると思うので、ここで間違って作成していません1つの電話番号?

著者の意図は100%ではありませんが、エンティティルックアップの要件がエンティティとそれに対応するVOの構造をどのように変更するかを説明しているだけだと思います。

トピックから外れていますが、貧弱なドメインモデルはエンティティからの行動の移動に起因すると考えましたが、あなたの例ではエンティティに多数の属性を設定しているため、顧客にあまりにも多くの行動が発生します(おそらく、これらの追加属性を変更する動作)、したがってSRPに違反していますか?

多くの属性は、多くの動作を意味するものではありません。実際、通常は反対のことを示唆しています。ゲッターとセッターを含む属性はたくさんありますが、カプセル化動作はありません。


更新を行いました
-EdvRusj

1

TL; DR:あなたはこれを考えすぎています。しかし、私はあなたと一緒にそれを再考するのが楽しかったです。だからバックル....

エンティティの単一の責任(変更する理由)は、それ自体を一意に識別することである必要があります。言い換えると、その責任を見つけられるようにすることです。

いいえ、そうではありません。エンティティの単一の責任は継続性です。

アイデンティティは、継続性の新たな結果です。分離可能なアイデアとしてのアイデンティティのモデリングは、パフォーマンスの最適化です。

例は次のとおりです。レストランのパトロンがバレットに車を渡します。1時間後、レストランの客が車を求めます。係員はそれを与えるべきですか?

後援者が「同じ」の場合、バレットは車を渡すべきだと言うのは簡単です。しかし、それは本当に何を意味するのでしょうか?それを判断する正しい方法は、「今」の利用者から始め、その利用者の履歴を後方に検索して、バレットに車を渡すことがその履歴の一部であるかどうかを確認することです。

もちろん、実際にそれを行うことはできません。私たちは自分自身の歴史を正確に追跡するのに苦労しています。いつも一緒にいなかった何かの歴史を気にしないでください。そのため、顧客の歴史を使用する代わりに、ショートカットを作成します。顧客は、現在キーに関連付けられているタグと同じ番号のチケットスタブを所有していますか?顧客の財布の運転免許証がDMVのタイトルの名前と一致しているか、運転免許証の写真が顧客の顔に似ているか。等。

要するに、利用者の履歴を確認する代わりに、利用者の現在の状態を確認し、現在の状態が自動車の到着から返品のリクエストまでの期間にわたる履歴と一致しているかどうかを確認します。

エンティティをモデリングする場合、類似の最適化を使用します。すべてのエンティティに共通の責任を与えます

  1. 履歴の先頭に、オブジェクトの状態への不変の識別子の割り当てが含まれていることを確認する
  2. 次の状態が常に前の状態の識別子の忠実なコピーを含むようにします。

ここでは、エンティティの2番目の責任について説明していません。エンティティは継続性に引き続き責任を負います-履歴が一貫した物語であることを確認します。識別子の責任は、すべてのエンティティに共通するサブセットにすぎません。

一意性の強制はまだありません。一意性にはすべてのエンティティの状態へのアクセスが必要なので、単一のエンティティ内では不可能です。単一のエンティティは、それ自体へのアクセスのみを持ちます。

繰り返しになりますが、毎回すべての識別子をチェックすることは実用的ではないため、代わりに一意性を簡単な方法で満たします。次の識別子を生成するコードは繰り返さないでください。

結局、これは、メモリ内の2つの異なる状態の部分の等価性をテストすることで連続性を検証できることを意味し、非循環グラフを照会しようとする手間を大幅に節約します。

また、単一責任原則(これは非常に良い考えです)と原子責任原則を混同しているようです。責任をより小さく、より管理しやすい部品に分解することは、SRPと互換性があります。


-3

まあ、それはあなたがそれを見たいかどうかに依存します。

別の方法は、「単一の責任原則がドメインエンティティに違反しているかどうか」です。

両方ともガイドラインです。ソフトウェア設計のどこにも「原理」はありません。しかし、良いデザインと悪いデザインがあります。これらの概念は両方とも、適切な設計を達成するためにさまざまな方法で使用できます。


説明できないダウン投票== SRPファンボーイ
h bob
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.