2장. 의존성 역전하기

2023. 4. 27. 19:35DDD/CleanArchitecture

소개

만들면서 배우는 클린 아키텍처 책을 읽고 정리하며 소감을 적는 포스트입니다.

의존성 역전하기

단일 책임 원칙(SRP, Single Responsibility Principle)

우리가 잘 알고 있는 정의는

하나의 컴포넌트는 오로지 한 가지 일만 해야하고, 그것을 올바르게 수행해야 한다.

하지만 실제 정의는

컴포넌트를 변경하는 이유는 오직 하나뿐이어야 한다.

만약 컴포넌트를 변경할 이유가 한 가지라면 우리가 어떤 다른 이유로 소프트웨어를 변경하더라도 이 컴포넌트에 대해서는 전혀 신경 쓸 필요가 없다.

어떤 컴포넌트의 의존성 각각이 이 컴포넌트를 변경하는 이유 하나씩에 해당 된다.

위 그림에서 점선 화살표는 전이 의존성(transitive dependency)이다.

E는 의존하는 것이 전혀 없지만 그에 비해 A의 경우는 B, C, 전의 의존 E, D를 가지고 있다.

A는 모든 컴포너트에 의존하기 때문에 다른 어떤 컴포넌트가 변경 되면 같이 변경되어야 한다.

많은 코드가 단일 책임 원칙을 위반하기 때문에 시간이 갈수록 변경하기가 더 어려워진다.

부수효과에 관한 이야기

대부분 코드의 한 영역을 변경했더니 다른 영역에서 부수효과(Side Effect)가 나타나기 일쑤이다.

의존성 역전 원칙

계층형 아키텍처에서 계층 간 의존성은 항상 다음 계층인 아래 방향을 가리킨다. 단일 책임 원칙을 고수준에서 적용할 때 상위 계층들이 하위 계층들에 비해 견경할 이유가 더 많다는 것을 알 수 있다.

영속성 계층을 변경할 때마다 잠재적으로 도메인 계층도 변경해야 한다.

그러나 도메인 코드는 애플리케이션에서 가장 중요한 코드이다. 영속성 코드가 바뀐다고 해서 도메인 코드까지 바꾸고 싶지는 않다.

이때 의존성 역전 원칙(DIP, Dependency Inversion Principle)가 답을 알려준다.

코드상의 어떤 의존성이드 그 방향을 바꿀 수(역전시킬 수)있다.

도메인 코드와 영속성 코드 간의 의존성을 역전시켜서 영속성 코드가 도메인 코드에 의존하고, 도메인 코드를 변경할 이유를 줄여보자

위 그림에서 도메인 계층에서 영속성 계층과 상호작용하는 서비스가 하나 있다.

엔티티는 도메인 객체를 표현하고 도메인 코드는 이 엔티티들의 상태를 변경하는 일을 중심으로 하기 때문에 엔티티를 먼저 도메인 계층으로 올린다.

여기서 엔티티가 도메인 계층으로 올라갔기 때문에 두 계층 사이에 순환 의존성(Circular dependency)가 생기게 됩니다.

이 부분이 바로 DIP를 적용하는 부분이다. 도메인 계층에 레포지토리에 대한 인터페이스를 만들고, 실제 리포지토리는 영속성 계층에서 구현하게 하는 것이다.

도메인 계층에 인터페이스를 도입함으로써 의존성을 역전시킬 수 있고, 그 덕분에 영속성 계층이 도메인 계층에 의존하게 된다.

도메인 계층에 리포지토리 인터페이스를 만들고, 실제 리포지토리는 영속성 계층에서 구현하게 하는 것이다.

클린 아키텍처

클린 아키텍처에서 모든 의존성은 도메인 로직을 향해 안쪽 방향으로 향한다.

로버튼 C.마틴은 클린 아키텍처에서는 설계까 비즈니스 규칙의 테스트를 용이하게 하고, 비즈니스 규칙은 프레임웤, 데이터베이스, UI 기술, 그 밖의 외부 애플리케이션이나 인터페이스로부터 독립적일 수 있다고 이야기 했습니다.

이는 도메인 코드가 바깥으로 향하는 어떤 의존성도 없어야 함을 의미하고 대신 의존성 역전 원칙의 도움으로 모든 의존성이 도메인 코드를 향하고 있어야 합니다.

클린 아키텍처 계열에서 도메인 계층과 애플리케이션 계층을 합쳐 application core라고 부릅니다.

아키텍처의 코어(core)에는 주변 유스케이스에서 접근하는 도메인 엔티티들이 있다. 유스케이스는 앞에서 서비스라고 불렀던 것들인데, 단일 책임을 갖기 위해 좀 더 세분화돼 있다. 이를 통해 이전에 이야기 했던 넓은 서비스 문제를 피할 수 있습니다.

클린 아키텍처는 도메인 계층이 영속성이나 UI같은 외부 계층과 철저하게 분리돼야 하므로 애플리케이션의 엔티티에 대한 모델을 각 계층에서 유지보수 해야한다.

가령 영속성 계층에서 ORM(object-relational mapping) 프레임워크를 사용하게 된다면 도메인 계층은 영속성 계층을 모르기 때문에 두 계층에서 각각 엔티티를 따로 만들어야 한다. 즉, 도메인 계층과 영속성 계층이 데이터를 주고 받을 때, 두 엔티티를 서로 변환해서 사용해야 한다.

육각형 아키텍처(헥사고날 아키텍처)

육각형 아키텍처는 애플리케이션 코어가 각 어댑터와 상호작용하기 위해 특정 포트를 제공하기 때문에 포트와 어댑터(ports-and-adapters) 아키텍처라고도 불리다.

애플리케이션 코어가 육각형으로 표현되어 이 아키텍처의 이름이 되었지만 육각형이나 팔각형이나 무엇이들 상관 없다.

육각형 안에는 도메인 엔티티와 이와 상호작용하는 유스케이스들이 있다. 육각형에서 외부로 향하는 의존성이 없기 때문에 마틴이 클린 아키텍처에서 제시한 의존성 규직이 그대로 적용 된다.

애플리케이션 코어와 어댑터들 간의 통신이 가능하려면 애플리케이션 코어가 각각의 포트를 제공해야 한다. 주도하는 어댑터(driving adapter)에게는 그러한 포트가 코어에 있는 유스케이스 클래스들에 의해 구현되고 호출되는 인터페이스가 될 것이고, 주도되는 어댑터(driven adapter)에게는 그러한 포트가 어댑터에 의해 구현되고 코어에 의해 호출 되는 인터페이스가 될 것이다.