複雑なアプリケーションでのDI。 中毒にdrれない方法

みなさんこんにちは。



アプリケーションを設計するときは、依存性注入(依存性注入)を使用することをお勧めします。 このアプローチにより、コードを疎結合にすることができ、これによりメンテナンスが容易になります。 テストも容易になり、コードは美しく、用途が広く、交換可能になります。 製品の開発では、この原則が最初から使用されてきました。高負荷DSPと企業ハイブリッドの両方です。 モジュールを作成し、さまざまなシステムとの統合を統合し、依存関係の数が増え、ある時点でアプリケーション構成自体を維持することが難しくなりました。 さらに、暗黙的な登録が追加され(たとえば、Web ApiのカスタムDependencyResolverがWeb Api設定で設定された)、構成モジュール呼び出し順序で問題が発生し始めました。 最後に、複雑なアプリケーションでモジュールを登録、構成、および初期化するためのアプローチを開発しました。 彼についてお話します。



画像



最初に、さまざまなタスク(単一の製品のフレームワーク内でも)を処理するために、サービス、コンソールアプリケーション、asp.netなどのいくつかの種類のアプリケーションがあることを明確にする必要があります。 したがって、初期化システムはどこでもその動物園を提示しましたが、味と色の依存関係のいまいましいクラウドを備えたDependencyConfigクラスがあるということだけがありました。 また、各アプリケーションには独自の追加設定がありました。 たとえば、asp.net mvcでルーティング、コンバーター、承認フィルターを設定します。これは、依存関係を登録し、この登録の正確性を確認した後に呼び出される必要があります。 したがって、タスクが発生しました:





その結果、依存関係、初期化(init)、および設定(実際には前の2つの組み合わせである設定)の3種類の基本構成を区別しました。



依存関係(IDependency)



依存関係は、1つのモジュールの依存関係を登録するためのプリミティブです。 通常、IDependencyインターフェイスを実装します。



public interface IDependency<TContainer> { void Register(TContainer container); }
      
      





TContainerはIoCコンテナーです(SimpleInjectorはコンテナーの例として以降で使用されます)。 したがって、1つの論理モジュールのサービスがRegisterメソッドに登録されます。 他のIDependencyプリミティブも、コンストラクターとRegisterメソッドを直接呼び出すことで登録できます。 例:



 public class TradingDeskDependency : IDependency<Container> { public void Register(Container container) { container.Register(() => new SwiffyClient(new SwiffyOptions{ MillisecondsTimeout = 20000 })); new DspIntegrationDependency().Register(container); } }
      
      





初期化(IInit)



初期化には、依存関係の登録および確認後、メインアプリケーションロジックの開始前に実行する必要があるコードが含まれます。 これは、asp.net mvcおよびweb apiまたはそのようなものをセットアップする可能性があります。 一般に、初期化クラスはIInitインターフェースを実装します。



 public interface IInit { void Init(IDependencyResolver resolver); }
      
      





例のように、依存関係から何らかのサービスを取得する必要がある場合、または依存関係自体を取得するメソッドを取得する必要がある場合は、IDependencyResolverが必要です。



 public class AspNetMvcInit: IInit { public void Init(IDependencyResolver resolver) { System.Web.Mvc.DependencyResolver.SetResolver(resolver.GetService, resolver.GetServices); new RouteInit().Init(resolver); } }
      
      





依存関係と同様に、ネストされた初期化プリミティブを使用できます。



設定(ISettings)



ロジックモジュールで依存関係の登録と初期化呼び出しの両方が必要な場合は、設定が必要です。 最も簡単に説明します。



  public interface ISettings<TContainer> : IDependency<TContainer>, IInit { }
      
      





したがって、論理モジュールを構成するためのすべての機能(依存関係の登録と追加設定の両方)を表す設定です。



一般的なデザイン



そのため、構成を分割できるプリミティブがありますが、管理を構成することは残ります。 これを行うには、IApplicationインターフェイスを実装するApplicationクラスが役立ちます。



 public interface IApplication<TContainer> { IApplication<TContainer> SetDependency<T>(T dependency) where T : IDependency<TContainer>; IApplication<TContainer> RemoveDependency<T>() where T : IDependency<TContainer>; IApplication<TContainer> SetInit<T>(T init) where T : IInit; IApplication<TContainer> RemoveInit<T>() where T : IInit; IApplication<TContainer> SetSettings<T>(T settings) where T : ISettings<TContainer>; IApplication<TContainer> RemoveSettings<T>() where T : ISettings<TContainer>; IAppConfig Build(); }
      
      





コードからわかるように、IApplicationを使用すると、すべてのタイプの設定を追加(および削除)できます。 そして、Buildメソッドは、これらのすべての設定を収集するコードを呼び出します。最初に、依存関係が登録され(必要であれば、すべてを登録するためのチェックが行われます)、コードはIInitモジュール(およびISettingsのInitメソッド)からです。 出力はIAppConfigオブジェクトです。



 public interface IAppConfig { IDependencyResolver DependencyResolver { get; } IAppLogger Logger { get; } }
      
      





DependencyResolverではサービスを受け取ることができ、Loggerではその理由がわかります。 アプリケーションをセットアップするための最終的なコードは、単純で透過的です(ただし、一般的な場合、普遍性のためにいくつかの問題があります)。



 var container = new Container() var appOptions = new AppOptions { DependencyContainer = container, GetServiceFunc = container.GetInstance, GetAllServicesFunc = container.GetAllInstances, VerifyAction = c => c.Verify(), Logger = new CustomLogger() }; var appConfig = new Application(appOptions).SetDependency(new TradingDeskDependency()) .SetInit(new AspNetMvcInit()) .Build();
      
      





明示的に定義する必要がある唯一のクラスはCustomLoggerです。 依存関係と初期化の登録が失敗した状況を追跡したい場合は、それを尋ねる必要があります。 ロガーは最も単純なインターフェースで記述されます:



 public interface IAppLogger { void Error(Exception e); }
      
      





実装を書くことは難しくありません。



その結果、このアプローチはあらゆるタイプのアプリケーションに使用でき、構成手順を考慮する必要はなく、依存関係管理では論理モジュールのレベルに移行しました。 したがって、思考と不屈の精神を維持しながら、多くの設定、依存関係、初期化を台無しにすることができます。



ライブラリ(Jdart.CoreApp)のわずかに単純化された(ただし非常に機能し、簡単に拡張可能な)バージョンを作成しました。

1) GitHub

2) ヌジェ



アダプターも利用可能です

1) SimpleInjector

2) Autofac

3) Ninject

4) 団結



どうもありがとう。



All Articles