[dependency-injection] Ioc / DI-응용 프로그램의 진입 점에서 모든 레이어 / 어셈블리를 참조해야하는 이유는 무엇입니까?

(이 질문과 관련하여 EF4 : 지연로드가 활성화 된 경우 프록시 생성을 활성화해야하는 이유는 무엇입니까? ).

저는 DI를 처음 사용하므로 참아주세요. 컨테이너가 등록 된 모든 유형의 인스턴스화를 담당하고 있지만 그렇게하려면 내 솔루션의 모든 DLL과 해당 참조에 대한 참조가 필요하다는 것을 이해합니다.

DI 컨테이너를 사용하지 않았다면 MVC3 앱에서 EntityFramework 라이브러리를 참조 할 필요가없고 내 DAL / Repo 레이어를 참조하는 비즈니스 레이어 만 참조 할 필요가 없습니다.

하루가 끝나면 모든 DLL이 bin 폴더에 포함되지만 내 문제는 필요한 모든 파일과 함께 WAP를 게시 할 수 있도록 VS의 “참조 추가”를 통해 명시 적으로 참조해야한다는 것을 알고 있습니다.



답변

DI 컨테이너를 사용하지 않았다면 MVC3 앱에서 EntityFramework 라이브러리를 참조 할 필요가없고 내 DAL / Repo 레이어를 참조하는 비즈니스 레이어 만 참조 할 필요가 없습니다.

네, 바로 DI가 피하기 위해 열심히 일하는 상황입니다. 🙂

밀접하게 결합 된 코드를 사용하면 각 라이브러리에 몇 개의 참조 만있을 수 있지만 여기에는 다시 다른 참조가 있으므로 다음과 같이 종속성에 대한 깊은 그래프를 생성합니다.

딥 그래프

종속성 그래프가 깊기 때문에 대부분의 라이브러리가 다른 많은 종속성 (예 : 다이어그램에서 라이브러리 C라이브러리 H, 라이브러리 E, 라이브러리 J, 라이브러리 M, 라이브러리 K라이브러리 N)을 따라 끌고 있음을 의미 합니다. 따라서 단위 테스트 에서와 같이 나머지 라이브러리와 독립적으로 각 라이브러리를 재사용하기가 더 어려워집니다 .

그러나 느슨하게 결합 된 응용 프로그램에서 모든 참조를 Composition Root 로 이동하면 종속성 그래프가 심하게 평면화됩니다 .

얕은 그래프

녹색으로 표시된 것처럼 이제 원치 않는 종속성을 끌지 않고 라이브러리 C 를 재사용 할 수 있습니다 .

그러나 많은 DI 컨테이너로 말했다 모든, 당신은하지 않습니다 필요한 모든 라이브러리 하드 참조를 추가 할 수 있습니다. 대신 규칙 기반 어셈블리 스캔 (선호) 또는 XML 구성의 형태로 후기 바인딩을 사용할 수 있습니다 .

그러나 그렇게 할 때 더 이상 자동으로 발생하지 않으므로 어셈블리를 응용 프로그램의 bin 폴더로 복사해야합니다. 개인적으로 나는 그다지 노력할 가치가 없다고 생각합니다.

이 답변에 대한 더 자세한 버전은 내 책 Dependency Injection, Principles, Practices, Patterns 에서 발췌 한 부분 에서 찾을 수 있습니다 .


답변

DI 컨테이너를 사용하지 않았다면 MVC3 앱에서 EntityFramework 라이브러리를 참조 할 필요가 없습니다.

DI 컨테이너를 사용하는 경우에도 MVC3 프로젝트가 EF를 참조하도록 할 필요는 없지만 (암시 적으로)이를 구현하여이를 수행하도록 선택합니다. Composition Root (객체 그래프를 구성하는 시작 경로) . 어셈블리를 사용하여 건축 경계를 보호하는 데 매우 엄격한 경우 프레젠테이션 논리를 다른 프로젝트로 이동할 수 있습니다.

모든 MVC 관련 로직 (컨트롤러 등)을 시작 프로젝트에서 클래스 라이브러리로 이동하면이 프레젠테이션 레이어 어셈블리가 나머지 애플리케이션과 연결이 끊어진 상태로 유지됩니다. 웹 애플리케이션 프로젝트 자체는 필요한 시작 로직을 가진 매우 얇은 쉘이됩니다. 웹 애플리케이션 프로젝트는 다른 모든 어셈블리를 참조하는 컴포지션 루트가됩니다.

프레젠테이션 로직을 클래스 라이브러리로 추출하면 MVC로 작업 할 때 상황이 복잡해질 수 있습니다. 컨트롤러는 시작 프로젝트에 없기 때문에 모든 것을 연결하기가 더 어려울 것입니다 (뷰, 이미지, CSS 파일은 시작 프로젝트에 남아 있어야 함). 이것은 아마도 가능하지만 설정하는 데 더 많은 시간이 걸립니다.

단점 때문에 일반적으로 웹 프로젝트에서 Composition Root를 유지하는 것이 좋습니다. 많은 개발자는 MVC 어셈블리가 DAL 어셈블리에 의존하는 것을 원하지 않지만 실제로는 문제가되지 않습니다. 어셈블리는 배포 아티팩트 라는 것을 잊지 마십시오 . 코드를 개별적으로 배포 할 수 있도록 코드를 여러 어셈블리로 분할합니다. 반면에 아키텍처 계층은 논리적 아티팩트입니다. 동일한 어셈블리에 여러 레이어를 포함하는 것이 매우 가능하고 일반적입니다.

이 경우 동일한 웹 애플리케이션 프로젝트 (따라서 동일한 어셈블리에 있음)에서 컴포지션 루트 (레이어)와 프레젠테이션 레이어를 갖게됩니다. 그리고 해당 어셈블리가 DAL을 포함하는 어셈블리를 참조하더라도 프레젠테이션 계층은 여전히 데이터 액세스 계층을 참조하지 않습니다. . 이것은 큰 차이입니다.

물론이 작업을 수행하면 컴파일러가 컴파일 타임에이 아키텍처 규칙을 확인할 수있는 기능을 잃게되지만 문제가되지는 않습니다. 대부분의 아키텍처 규칙은 실제로 컴파일러에서 확인할 수 없으며 항상 상식과 같은 것이 있습니다. 그리고 팀에 상식이 없다면 언제든지 코드 리뷰를 사용할 수 있습니다 (모든 팀은 항상 btw를 수행해야합니다). 아키텍처 규칙을 확인하는 데 도움이되는 NDepend (상업용)와 같은 도구를 사용할 수도 있습니다. NDepend를 빌드 프로세스와 통합하면 누군가 이러한 아키텍처 규칙을 위반하는 코드를 체크인했을 때 경고 할 수 있습니다.

내 책 Dependency Injection, Principles, Practices, Patterns의 4 장에서 컴포지션 루트가 작동하는 방법에 대한 자세한 토론을 읽을 수 있습니다 .


답변

DI 컨테이너를 사용하지 않았다면 MVC3 앱에서 EntityFramework 라이브러리를 참조 할 필요가없고 내 DAL / Repo 레이어를 참조하는 비즈니스 레이어 만 참조 할 필요가 없습니다.

“DependencyResolver”라는 별도의 프로젝트를 만들 수 있습니다. 이 프로젝트에서는 모든 라이브러리를 참조해야합니다.

이제 UI 레이어는 참조 할 캐슬 윈저를 제외하고 NHibernate / EF 또는 다른 UI 관련 라이브러리가 필요하지 않습니다.

UI 계층에서 Castle Windsor 및 DependencyResolver를 숨기려면 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 레지스트리 설정된다.


답변

  • 종속성이 있습니다. 객체가 다른 객체를 인스턴스화하는 경우.
  • 종속성이 없습니다. 객체가 추상화를 기대하는 경우 (컨 트럭 터 주입, 메서드 주입 …)
  • 어셈블리 참조 (dll, webservices .. 참조)는 추상화를 해결하고 코드를 컴파일 할 수 있으려면 계층에서 참조해야하기 때문에 종속성 개념과는 별개입니다.

답변