web-dev-qa-db-ja.com

依存性注入フレームワークによるエンティティの構築

私は意見の不一致、同僚を解決するのに助けが必要です。私たちはかなり巨大なAngularプロジェクトに取り組んでおり、プロジェクトにドメイン主導設計の原則を組み込むことを検討中です。

この図を見ながら、依存性注入フレームワークがこれにどのように影響を与えるかについて説明しました。

enter image description here

私のスタンス:依存性注入フレームワークは、アプリケーションサービス、ドメインサービス、リポジトリ、およびファクトリにのみ使用する必要があります。一方、ドメインオブジェクトは基本的に新規可能であり(ファクトリー内を除いて通常は新規作成されません)、コンストラクター自体の依存関係はありません。
彼のスタンス:これは不可能です。DIフレームワークがあり、それを使用する必要があるためです。彼の恐れは巨大な一枚岩のクラスです。クラスがあまりにも多くのメソッドを持っている場合、これらを(SRPのために)より小さなクラスに分割し、それらをある種の計装クラスにバンドルすることは完全に正常であると彼は主張します。これは、依存性注入のない雑用になります。彼は、DI-Frameworksはすべてのより大きなプロジェクトで使用するのが普通であり、可能な限り使用しないのはばかげていると主張します。
なぜ彼を説得するのがとても難しいのですか:私たちはテーブルをよく扱い、多かれ少なかれ表現するクラスがありますテーブルの「モデル」(一瞬無視してください。ほとんどのプロジェクトでは、「テーブル」は実際には問題ドメインの一部ではなく、前提条件を受け入れます。それは私たちのものである可能性があります)。まず最初に、たとえデータとテーブルが単純であっても、テーブルを表すクラスには数十のメソッドが含まれます。行の追加と削除、列のサイズ変更、行の選択などの状態の取得と設定、列の強調表示の間。
さらに、行を追加するなど、従来のように単純なものであっても、多くの場合、テーブルの他の部分に5つの異なる副作用があります(これらは実際の要件です)。したがって、4000行のTableクラスを持つことは受け入れられないということは、皆さんも同意できると思います。結果として、その機能の一部を小さなクラスに移動する必要があります。これは、1つのことだけを行うか、1つのことの一部を行うだけです。そして今、私たちは行き詰まりにいます。これは依然、依存性注入のないクラスである必要があると思いますが、私はそれを行う方法がわかりません、そして彼は断固として、私はこれを問題として想像しているだけで、クラスにサービスをフィナリングして、仕事をしたいと思っています、それは通常それ自体で行うべきです。

それで私は狂っていますか?彼は狂っていますか?さらに、彼に同意しない場合、「誰もがstackoverflowに何でも書くことができる」ため、信頼できるソースをリンクすることでボーナスポイントを獲得できます

3
KeTr

[〜#〜] yagni [〜#〜] に導かれるとしたら、ドメインオブジェクトに依存関係の注入を導入することになるとは思いません。

依存性注入は戦略パターンの現れです。依存関係を挿入できることの価値は、1つの動作を別の動作に置き換えることができることです。もちろん、ある振る舞いを別の振る舞いに置き換える簡単な方法は、問題のメソッドを書き直すことです-したがって、reallyが意味することは、 2つ(またはそれ以上)の候補動作をサポートし、外部からその選択を行うことができます。

古典的な例は、副作用を伴うインターフェースです-例えば、電子メールを送信することです。本番環境では、実際にはSMTPサーバーに接続します。テストでは、ふりをします。したがって、外部からの動作を制御できるようにする必要があります。

ドメインオブジェクトは、モデル化しているビジネスのメモリ表現にあります。 (直接)副作用はありません。ドメインオブジェクトでメソッドを呼び出すと、通常、プロセスのプライベートメモリからの読み取りまたは書き込みだけが行われます。

ドメインモデルによって行われる作業の一部として副作用を含める場合は、呼び出す機能に引数として機能を渡します。これは、ドメインサービスの役割の1つです。つまり、外部世界のプロキシとして機能します。

ドメインモデル自体には副作用がないため、通常、テスト対象をその環境から分離することで得られる利点は適用されません。ドメインロジックが非常に複雑でない限り、コールグラフを単純化することで多くを得ることができません。

テストバリエーションの詳細については、Ian Cooperの講演 TDD:どこがうまくいかなかったか を参照してください。

興味深いと思われるもう1つのバリエーションは、データ構造のバリエーションです。ドメインモデルのこの値がリンクリストまたは配列リスト、あるいはヒープである必要があります。..ある程度、「値オブジェクト」とは、ドメインロジックからデータをメモリに配置する方法に関する懸念を抽象化することです。

型について特定する必要がある言語(Javaなど)では、値オブジェクトのグラフの置換をサポートするために作成する必要があるインターフェースは複雑です。型のバリエーションはカプセル化されません。だからあなたは本当にそれを必要とする必要があります。

ドメインモデルに依存性注入を導入する前に、必要性が実証される前に、投資オッズはお粗末です。問題は、見返りの可能性が小さいことであり、早期に作業を行っても見返り/コストの比率は大幅には改善されません。

これは...間違ったツールです。 -ザスラ

リンク集

4
VoiceOfUnreason

このような状況では、「なぜこのパターン/フレームワーク/ツールを使用するのか」という質問が1つ戻って質問するのに役立つと思います。一般的なパターン、特にDIに関する混乱の多くは、特定のコスト/メリットの図なしでパターンを使用するという多くのプレッシャーから本当に生じていることがわかりました。

依存関係注入の主な利点は、クラスをその依存関係から切り離し、依存関係によって提供される機能の異なる実装を使用できる新しいコンテキストでクラスを使用できるようにすることです。 (ウィキペディアはこれを https://en.wikipedia.org/wiki/Dependency_injection#Advantages でさらに詳しく説明しています)正規の例は永続化レイヤーです。今日、私のサービスはSQLデータベースからデータを取得する可能性がありますが、明日はWebサービスからデータをプルしたいと思うかもしれません。

聞く利点のほとんどは、依存関係の実装を置き換えるこの機能の特殊なケースです。実際には、DIを使用するほとんどのコードベースが単体テストでそうしていることがわかります。サービスコンポーネントがインジェクトされるリポジトリコンポーネントに依存している場合は、ユニットテストのためにリポジトリをモックまたはスタブに置き換える方が簡単です。

明確にするために、DIにも欠点があります。最大の欠点は、複雑さが増すことで、実行中のシステムのオーバーヘッドがごくわずかになり、間接参照の層が増えるため、コードを読み取るときに複雑さが増すことです。

DIが解決しようとしている問題が表示されたので、DIが解決しない問題、つまりオブジェクトの構築を明確にすることができます。 newに問題はありません。壊れていません。それはそれが缶に言うことをします:それはクラスの新しいインスタンスを作成します。したがって、オブジェクトを作成するだけでは、DIを使用する十分な理由にはなりません。機能の実装を置き換える機能を解決する必要があります。

モデル/データオブジェクトの場合はそうですか?通常はありません。通常、これらはデータを表し、いかなる種類の機能も比較的ほとんどありません。注入するものが何もない場合は、依存性注入を使用する理由はありません。 (明確にするために、単にオブジェクトを作成することは実際には同じではなく、コンポーネントの依存関係を表すことに注意してください。PersonにはAddressがあるかもしれませんが、それは単なる属性かもしれませんa Person-PersonAddressに依存することを意味する必要はありません。

抽象的ではなく、理論的ではないメモでは、多くの依存関係注入ライブラリ/フレームワークがシングルトンを多用することを認識することも重要です(この動作は、使用中のDIフレームワークとその構成方法に依存します(たとえば、Springはシングルトンです)。デフォルトでは、他の動作で設定することもできます;スタックを知る必要があるだけです)。実際、アクティブなインスタンスの削減は、メリットとして挙げられることがあります。つまり、同じモデル/データオブジェクトを変更する複数のコードに誤って巻き込まれる可能性があります。これが発生すると、あらゆる種類のランダムな問題と競合状態が発生する可能性があります。そうでない場合は、実際にはDIフレームワークを使用してファクトリを実装しているだけであり、ファクトリが構成コードから分離されていると、コードベースのメンテナにとってより明確になる可能性があります。

これが最後の要点です。依存関係の注入は、構成の概念と密接に結びついており、システムの構成はさまざまなオブジェクトグラフを生成します。通常、モデル/データクラスの構成方法は多くありません。

概要:依存関係の注入を介して構築されたモデルまたはデータオブジェクトは、通常、依存関係のあるコンポーネントを表さず、また通常はそうでないので、それを見るのは独特です構成に敏感な側面があります。最後に、インスタンスを定期的に作成して破棄することを期待しますが、これはほとんどのDIフレームワークのシングルトンファーストの考え方ではうまく機能しません。

Martin Fowlerによるパターンの説明( https://martinfowler.com/articles/injection.html )とStackOverflowに関するディスカッション( https:// stackoverflow)の一部を読むことをお勧めします.com/questions/871405/why-do-i-need-an-ioc-container-as-opposed-to-straightforward-di-code 私にとって最も興味深いものの1つ) DIは、特定のユースケースに適用される場合と適用されない場合の理由です。

3
Michael

ここで追加したいポイントのカップル:

あなたはどちらも正しい私見です。巨大な「神」クラスを回避し、SRPに違反するというトピックについての同僚と、構成可能なコンポーネントをハイドレイトすべきだがおそらくドメインオブジェクトではないDIフレームワークの基本的な使用法について。

多分あなたはあなたが両方が同意する解決策を考え出すことができます。

  • DIは、フレームワークまたはIoCコンテナーの使用を意味するものではありません。本質的に、自分で依存関係を(newまたはelse経由で)構築していないので、機能するために必要なものをすべて初期化するコンストラクタを持つクラスはすでにDIです。
  • sRPに従って、「神」のクラスを複数のクラス(または「特性」の特性を持つインターフェース)に分解します。可能であれば、以前の神のクラスは、多数の値オブジェクト/エンティティを構成するだけであり、それぞれに重要な分離されたロジックの断片が含まれています。
  • DDDは冗長性を避けません。私は、1つのクラスでいくつかのバインドされたコンテキストが重複する巨大なPersonエンティティを含む(非ddd)プロジェクトを目撃しました。実際のエンティティが複数のコンテキストに属している場合は、それを複数回モデル化し、結果のエンティティ/集計でこのコンテキストの関連する側面をカバーします(これにより、データ/フィールドに「冗長性」が認識される可能性があります)
0
winson