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


123

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

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

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

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


1
.NET의 Dependency Injection 책에서 발췌 한이 두 번째 버전 은 Mark와 저의 답변에 대한보다 정교한 버전입니다. 컴포지션 루트 의 개념 과 응용 프로그램의 시작 경로가 다른 모든 모듈에 의존하도록하는 것이 실제로 좋은 이유 를 자세히 설명합니다 .
Steven

저는 그 발췌 링크와 1 장을 읽었습니다. 저는 DI의 복잡한 문제에 대한 비유와 간단한 설명을 정말로 즐 겼기 때문에 책을 구입할 것입니다. 새로운 답변을 제안해야한다고 생각합니다. "컴포지션 루트가 아니라면 엔트리 논리 레이어의 모든 레이어 / 어셈블리를 참조 할 필요가 없습니다."라고 명확하게 대답하고 발췌 부분에 링크 한 다음 그림 3의 이미지를 게시해야합니다. 발췌.
diegohb

답변:


194

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

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

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

딥 그래프

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

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

얕은 그래프

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

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

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

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


3
정말 감사합니다. 이제 완벽하게 이해됩니다.이게 의도 된 것인지 알고 싶었습니다. 종속성의 올바른 사용을 시행하는 한, 나머지 라이브러리를 참조하는 Steven과 같은 DI 부트 스트 래퍼로 별도의 프로젝트를 구현했습니다. 이 프로젝트는 진입 점 앱에서 참조되며 전체 빌드가 끝나면 필요한 모든 dll이 bin 폴더에있게됩니다. 감사!
diegohb

2
@Mark Seemann이 질문 / 답변은 Microsoft에만 해당됩니까? 모든 종속성을 "응용 프로그램의 진입 점"으로 이동하는이 아이디어가 Maven을 사용하는 Java EE / Spring 프로젝트에 적합한 지 알고 싶습니다. 감사합니다!
Grégoire C

5
이 답변은 .NET 이외에도 적용됩니다. 당신은 로버트 C. 마틴을 참조 할 수 있습니다 패키지 디자인의 원리 예에 장 애자일 소프트웨어 개발, 원칙, 패턴 및 실행
마크 시만

7
@AndyDangerGagne 컴포지션 루트는 서비스 로케이터반대 인 DI 패턴 입니다. 컴포지션 루트의 관점에서 볼 때 어떤 유형도 다형성이 아닙니다. Composition Root는 모든 유형을 구체적인 유형으로 간주하므로 Liskov Substitution Principle이 적용되지 않습니다.
Mark Seemann

4
일반적으로 인터페이스는이를 사용하는 클라이언트 ( APP, ch. 11 )에 의해 정의되어야 하므로 라이브러리 J에 인터페이스가 필요한 경우 라이브러리 J에서 정의해야합니다. 이는 종속성 반전 원칙의 결과입니다.
마크 시만

65

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 장에서 컴포지션 루트가 작동하는 방법에 대한 자세한 토론을 읽을 수 있습니다 .


부트 스트랩을위한 별도의 프로젝트는 ndepend가없고 이전에 사용한 적이 없기 때문에 내 솔루션이었습니다. 최종 애플리케이션이 하나만있을 때 내가하려는 작업을 수행하는 더 좋은 방법처럼 들리기 때문에 살펴 보겠습니다.
diegohb

1
마지막 단락은 훌륭한 단락이며 별도의 어셈블리에 레이어를 유지하는 데 얼마나 엄격한 지 생각을 바꾸는 데 도움이되기 시작했습니다. UI 코드에서 DAL 클래스를 참조하지 않도록 코드 작성과 관련하여 다른 프로세스 (예 : 코드 검토)를 사용하는 경우 하나의 어셈블리에 두 개 이상의 논리 계층이있는 것이 좋습니다.
BenM 2015 년

6

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 레지스트리 설정된다.


1
나는 이것이 내가 결국했던 것보다 훨씬 더 좋아한다. 모듈은 훌륭하다. 그럼 어디에서 Container.Dispose ()를 호출할까요? 모듈 내의 ApplicationEnd 또는 EndRequest 이벤트 ...?
diegohb

1
@Steven Global.asax가 MVC UI 레이어에 있기 때문입니다. HttpModule은 DependencyResolver 프로젝트에 있습니다.
Rookian

1
작은 이점은 아무도 UI에서 IoC 컨테이너를 사용할 수 없다는 것입니다. 즉, 누구도 UI에서 IoC 컨테이너를 서비스 로케이터로 사용할 수 없습니다.
Rookian

1
또한 UI에 어셈블리에 대한 하드 참조가 없기 때문에 개발자가 UI 레이어에서 실수로 DAL 코드를 사용하는 것을 방지합니다.
diegohb

1
Bootstrapper의 일반 등록 API를 사용하여 동일한 작업을 수행하는 방법을 알아 냈습니다. 내 UI 프로젝트는 등록을 연결하는 종속성 해결 프로젝트 인 부트 스트 래퍼와 내 코어 (인터페이스 용)의 프로젝트를 참조하지만 DI 프레임 워크 (SimpleInjector)도 참조하지 않습니다. OutputTo nuget을 사용하여 dll을 bin 폴더에 복사하고 있습니다.
diegohb 2013 년

0
  • 종속성이 있습니다. 객체가 다른 객체를 인스턴스화하는 경우.
  • 종속성이 없습니다. 객체가 추상화를 기대하는 경우 (컨 트럭 터 주입, 메서드 주입 ...)
  • 어셈블리 참조 (dll, webservices .. 참조)는 추상화를 해결하고 코드를 컴파일 할 수 있으려면 계층에서 참조해야하기 때문에 종속성 개념과는 별개입니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.