Ioc / DI-アプリケーションのエントリポイントですべてのレイヤー/アセンブリを参照する必要があるのはなぜですか?


123

(この質問に関連して、EF4:遅延読み込みが有効になっているときにプロキシの作成を有効にする必要があるのはなぜですか?

私はDIが初めてなので、我慢してください。コンテナーがすべての登録済みタイプのインスタンス化を担当していることを理解していますが、そのためには、ソリューション内のすべてのDLLへの参照とその参照が必要です。

DIコンテナーを使用していない場合は、MVC3アプリでEntityFrameworkライブラリを参照する必要はなく、DAL / Repoレイヤーを参照するビジネスレイヤーのみを参照します。

結局のところ、すべてのDLLがbinフォルダーに含まれていることを知っていますが、私の問題は、必要なすべてのファイルを含むWAPを公開できるように、VSの「参照の追加」を介して明示的に参照する必要があることです。


1
.NETの依存性注入」の第2版からのこの抜粋は、Markと私自身の回答のより精巧なバージョンです。これは、コンポジションルートの概念と、アプリケーションの起動パスを他のすべてのモジュールに依存させることが実際に良いことである理由を詳しく説明しています。
スティーブン

その抜粋のリンクと第1章を読み、DIの複雑な問題の類推と簡単な説明を本当に楽しんでいるので、この本を購入します。新しい答えを提案し、「コンポジションルートでもない限り、エントリの論理レイヤー内のすべてのレイヤー/アセンブリを参照する必要はありません」と明確に答え、抜粋へのリンクを貼って、図3の画像を投稿してください。抜粋。
diegohb

回答:


194

DIコンテナーを使用していない場合は、MVC3アプリでEntityFrameworkライブラリを参照する必要はなく、DAL / Repoレイヤーを参照するビジネスレイヤーのみを参照します。

はい、それはまさにDIが回避するのがとても難しい状況です:)

緊密に結合されたコードでは、各ライブラリには参照がわずかしか含まれない場合がありますが、これらには再び他の参照があり、次のような依存関係の深いグラフが作成されます。

深いグラフ

依存関係グラフが深いため、ほとんどのライブラリは他の多くの依存関係に沿ってドラッグします。たとえば、図では、ライブラリCがライブラリH、ライブラリE、ライブラリJ、ライブラリM、ライブラリKおよびライブラリNに沿ってドラッグします。これにより、たとえば単体テストなど、各ライブラリを他のライブラリから独立して再利用することが難しくなります

ただし、疎結合アプリケーションでは、すべての参照をコンポジションルートに移動することにより、依存関係グラフが大幅にフラット化されます。

浅いグラフ

緑色で示されているように、不要な依存関係に沿ってドラッグせずにライブラリCを再利用できるようになりました。

しかし、多くのDIコンテナと、言ったことすべては、あなたがいない持っているすべての必要なライブラリへのハード参照を追加します。代わりに、コンベンションベースのアセンブリスキャン(推奨)またはXML構成のいずれかの形式でレイトバインディングを使用できます。

ただし、これを行うときは、アセンブリをアプリケーションのbinフォルダーにコピーすることを忘れないでください。これは、自動的に実行されないためです。個人的に、私はそれがその余分な努力の価値があることをめったに見つけません。

この回答のより複雑なバージョンは、私の著書「依存性注入、原則、実践、パターン」からのこの抜粋にあります。


3
おかげで、これは今では完全に理にかなっています。これが仕様によるものかどうかを知る必要がありました。依存関係の正しい使用を強制する限り、私は、後述するStevenのようにDIブートストラップを使用して別のプロジェクトを実装し、残りのライブラリを参照しました。このプロジェクトはエントリポイントアプリによって参照され、フルビルドの最後に、必要なすべてのdllがbinフォルダーに置かれます。ありがとう!
diegohb 2012年

2
@Mark Seemannこの質問/回答はMicrosoftに固有のものですか?すべての依存関係を「アプリケーションのエントリポイント」に移動するというこの考えが、Mavenを使用したJava EE / Springプロジェクトにとって意味があるかどうか知りたい…ありがとう!
グレゴワールC

5
この回答は.NETを超えて適用されます。ロバートC.マーティンの「パッケージデザイン原則」の章(たとえば、アジャイルソフトウェアの開発、原則、パターン、および実践
Mark Seemann、

7
@AndyDangerGagneコンポジションルートはDIパターンであり、Service Locatorの反対です。コンポジションルートの観点から見ると、どのタイプもポリモーフィックではありません。コンポジションルートはすべてのタイプを具象タイプと見なすため、リスコフの置換原則は適用されません。
マークシーマン2014

4
一般的なルールとして、インターフェースはそれらを使用するクライアントによって定義される必要があります(APP、ch。11)。したがって、ライブラリJがインターフェースを必要とする場合、それはライブラリJで定義される必要があります。これは依存関係逆転原理の当然の帰結です。
Mark Seemann 2014年

65

DIコンテナーを使用していない場合、MVC3アプリでEntityFrameworkライブラリを参照する必要はありません。

DIコンテナーを使用している場合でも、MVC3プロジェクトにEFを参照させる必要はありませんが、MVC3プロジェクト内にコンポジションルート(オブジェクトグラフを作成するための起動パス)を実装することによって(暗黙的に)これを行うことを選択します。アセンブリを使用してアーキテクチャの境界を保護することに非常に厳しい場合は、プレゼンテーションロジックを別のプロジェクトに移動できます。

MVC関連のすべてのロジック(コントローラーなど)をスタートアッププロジェクトからクラスライブラリに移動すると、このプレゼンテーションレイヤーアセンブリを他のアプリケーションから切断したままにすることができます。Webアプリケーションプロジェクト自体は、必要な起動ロジックを持つ非常に薄いシェルになります。Webアプリケーションプロジェクトは、他のすべてのアセンブリを参照するコンポジションルートになります。

プレゼンテーションロジックをクラスライブラリに抽出すると、MVCでの作業が複雑になる可能性があります。コントローラーはスタートアッププロジェクトに含まれていないため(ビュー、イメージ、CSSファイルはおそらくスタートアッププロジェクトにとどまっている必要があるため)、すべてを接続するのは困難です。これはおそらく実行可能ですが、設定に時間がかかります。

不利な点があるため、私は通常、コンポジションルートをWebプロジェクトに保持することをお勧めします。多くの開発者は、MVCアセンブリがDALアセンブリに依存することを望んでいませんが、それは実際には問題ではありません。アセンブリはデプロイメントアーティファクトであることを忘れないでください。コードを複数のアセンブリに分割して、コードを個別にデプロイできるようにします。一方、建築レイヤーは論理的です成果物です。同じアセンブリに複数のレイヤーを含めることは非常によく可能です(そして一般的です)。

この場合、最終的にはコンポジションルート(レイヤー)とプレゼンテーションレイヤーが同じWebアプリケーションプロジェクトに(つまり、同じアセンブリに)含まれることになります。また、そのアセンブリはDALを含むアセンブリを参照していますが、プレゼンテーションはデータアクセス層を参照していません。これは大きな違いです。

もちろん、これを行うと、コンパイラーがコンパイル時にこのアーキテクチャー規則をチェックする機能を失いますが、これは問題にはなりません。ほとんどのアーキテクチャルールは実際にはコンパイラでチェックすることができず、常識のようなものが常にあります。また、チームに常識がない場合は、常にコードレビューを使用できます(すべてのチームが常にIMOで行う必要があります)。NDepend(商用)のようなツールを使用することもできます。これは、アーキテクチャルールの検証に役立ちます。NDependをビルドプロセスに統合すると、そのようなアーキテクチャルールに違反するコードを誰かがチェックインしたときに警告が表示されます。

あなたは私の本のDependency Injection、Principles、Practices、Patternsの 4章で、Composition Rootがどのように機能するかについてのより複雑な議論を読むことができます。


ndependがなく、これまで使用したことがないため、ブートストラップ用の別のプロジェクトが私の解決策でした。ただし、エンドアプリケーションが1つしかない場合に、私がやろうとしていることを達成するためのより良い方法のように思われるので、それを調べます。
diegohb 2012年

1
最後の段落は素晴らしいものであり、レイヤーを別々のアセンブリに保持することの厳格さについての考え方を変える手助けをし始めています。コードの記述に他のプロセス(コードレビューなど)を使用して、UIコードでDALクラスを参照しないようにしたり、その逆を行ったりする場合は、1つのアセンブリに2つ以上の論理レイヤーがあることは実際には問題ありません。
BenM

6

DIコンテナーを使用していない場合は、MVC3アプリでEntityFrameworkライブラリを参照する必要はなく、DAL / Repoレイヤーを参照するビジネスレイヤーのみを参照します。

「DependencyResolver」という別のプロジェクトを作成できます。このプロジェクトでは、すべてのライブラリを参照する必要があります。

これで、UIレイヤーでNHibernate / EFや、参照するCastle Windsor以外のUIに関連しないその他のライブラリは必要なくなりました。

Castle WindsorとDependencyResolverをUIレイヤーから非表示にしたい場合は、IoCレジストリなどを呼び出すHttpModuleを記述できます。

StructureMapの例しかありません。

public class DependencyRegistrarModule : IHttpModule
{
    private static bool _dependenciesRegistered;
    private static readonly object Lock = new object();

    public void Init(HttpApplication context)
    {
        context.BeginRequest += (sender, args) => EnsureDependenciesRegistered();
    }

    public void Dispose() { }

    private static void EnsureDependenciesRegistered()
    {
        if (!_dependenciesRegistered)
        {
            lock (Lock)
            {
                if (!_dependenciesRegistered)
                {
                    ObjectFactory.ResetDefaults();

                    // Register all you dependencies here
                    ObjectFactory.Initialize(x => x.AddRegistry(new DependencyRegistry()));

                    new InitiailizeDefaultFactories().Configure();
                    _dependenciesRegistered = true;
                }
            }
        }
    }
}

public class InitiailizeDefaultFactories
{
    public void Configure()
    {
        StructureMapControllerFactory.GetController = type => ObjectFactory.GetInstance(type);
          ...
    }
 }

DefaultControllerFactoryはIoCコンテナを直接使用しませんが、IoCコンテナメソッドに委譲します。

public class StructureMapControllerFactory : DefaultControllerFactory
{
    public static Func<Type, object> GetController = type =>
    {
        throw new  InvalidOperationException("The dependency callback for the StructureMapControllerFactory is not configured!");
    };

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            return base.GetControllerInstance(requestContext, controllerType);
        }
        return GetController(controllerType) as Controller;
    }
}

GetControllerデリゲートは、(ウィンザーでは、インストーラであるべきである)のStructureMapレジストリに設定されています。


1
私はこれが私がやったことよりもさらに好きです、モジュールは素晴らしいです。それでは、どこでContainer.Dispose()を呼び出しますか?モジュール内のApplicationEndまたはEndRequestイベント...?
diegohb 2012年

1
@Steven Global.asaxがMVC UIレイヤーにあるからです。HttpModuleはDependencyResolverプロジェクトにあります。
Rookian

1
小さな利点は、UIでIoCコンテナーを誰も使用できないことです。つまり、UIでIoCコンテナをサービスロケータとして使用することはできません。
Rookian

1
また、UIのアセンブリへのハード参照がないため、開発者が誤ってUIレイヤーでDALコードを使用することを禁止します。
diegohb

1
Bootstrapperの汎用登録APIを使用して同じことを行う方法を理解しました。私のUIプロジェクトは、Bootstrapperを参照しています。これは、登録を結び付ける依存関係解決プロジェクトであり、Core内のプロジェクト(インターフェイス用)だけですが、DIフレームワーク(SimpleInjector)も参照していません。OutputTo nugetを使用してdllをbinフォルダーにコピーしています。
diegohb 2013

0
  • 依存関係があります。オブジェクトが別のオブジェクトをインスタンス化する場合。
  • 依存関係はありません。オブジェクトが抽象化を期待する場合(コンストラクターの注入、メソッドの注入など)
  • 抽象化を解決してコードをコンパイルできるようにするには、レイヤーがそれを参照する必要があるため、アセンブリ参照(dll、webservices ..を参照)は依存関係の概念から独立しています。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.