본문 바로가기
📙/Study

[만들면서 배우는 클린 아키텍처] 02. 의존성 역전하기

by kiwi_wiki 2024. 10. 5.

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

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

 

만약 컴포넌트를 변경할 이유가 한 가지라면 우리가 어떤 다른 이유로 소프트웨어를 변경하더라도 이 컴포넌트에 대해서는 전혀 신경 쓸 필요가 없다. 변경할 이유라는 것은 컴포넌트 간의 의존성을 통해 너무 쉽게 전파된다.

 

점선 화살표처럼 전이 의존성이라고 하더라도 어떤 컴포넌트의 의존성은 각각 이 컴포넌트를 변경하는 이유 하나씩에 해당한다.

 

컴포넌트 A는 다른 여러 컴포넌트에 의존하는 반면 컴포넌트 E는 의존하는 것이 없다.

컴포넌트 E를 변경할 유일한 이유는 새로운 요구사항에 의해 E의 기능을 바꿔야 할 때뿐이다. 반면 컴포넌트 A의 경우에는 모든 컴포넌트에 의존하고 있기 때문에 다른 어떤 컴포넌트가 바뀌든지 같이 바뀌어야 한다.

 

시간이 지날수록 변경하기가 더 어려워지고 변경 비용도 증가한다. 변경할 이유가 많이 쌓여가고 그렇게 되면 한 컴포넌트를 바꾸는 것이 다른 컴포넌트가 실패하는 원인으로 작용할 수 있다.

부수효과에 관한 이야기

과거에 핵심적인 특정 컴포넌트를 변경했을 때 다른 무언가가 망가지는 부수효과를 경험한 클라이언트는 조금 더 사용자 친화적이면서도 변경 비용이 저렴한 방식이 존재하더라도 핵심 컴포넌트를 변경하지 않는 복잡한 방식으로 구현되길 바랄 수 있다.

의존성 역전 원칙 (Dependency Inversion Principle, DIP)

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

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

 

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

양쪽 코드를 모두 제어할 수 있을 때만 의존성을 역전시킬 수 있다. 서드파티 라이브러리에 의존성이 있다면 해당 라이브러리를 제어할 수 없기 때문에 이 의존성은 역전시킬 수 없다.

도메인 계층에 인터페이스를 도입함으로써 의존성을 역전시킬 수 있다

도메인 계층에 리포지토리에 대한 인터페이스를 만들고, 실제 리포지토리는 영속성 계층에서 구현해 의존성을 역전시킴으로 도메인 코드를 변경할 이유의 개수를 줄일 수 있다.

클린 아키텍처

설계가 비즈니스 규칙의 테스트를 용이하게 하고,

비즈니스 규칙은 프레임워크, 데이터베이스, UI 기술, 그 밖의 외부 애플리케이션이나 인터페이스로부터 독립적일 수 있다.

 

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

 

이 아키텍처의 코어에는 주변 유스케이스에서 접근하는 도메인 엔티티들이 있다. 유스케이스는 서비스라고 불리는 것들로 단일 책임을 갖기 위해 세분화되어있다. 이를 통해 넓은 서비스 문제를 피할 수 있다.

 

코어 주변으로 비즈니스 규칙을 지원(UI, 영속성 등 제공)하는 애플리케이션의 다른 모든 컴포넌트들을 확인할 수 있다. 바깥쪽 계층들은 다른 서드파티 컴포넌트에 어댑터를 제공할 수 있다.

 

도메인 코드에서는 어떤 영속성 프레임워크가 사용되는 지 알  수 없기 때문에 특정 프레임워크에 특화된 코드를 가질 수 없고 비즈니스 규칙에 집중할 수 있다. 그래서 도메인 코드를 자유롭게 모델링할 수 있다.

 

 

 

애플리케이션의 엔티티에 대한 모델을 각 계층에서 유지보수 해야 한다.

영속성 계층에서 ORM 프레임워크를 사용하는 경우 데이터베이스 구조 및 컬럼의 매핑을 서술한 메타데이터를 담고 있는 엔티티 클래스를 필요로 한다. 도메인 계층은 영속성 계층을 모르기 때문에 도메인 계층에서 사용한 엔티티 클래스를 영속성 계층에서 함께 사용할 수 없고 두 계층에서 각각 엔티티를 만들어야 하며 계층 간에 데이터를 주고받을 때 두 엔티티를 변환해야 한다. 이는 바람직한 일이다.

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

육각형 아키텍처. 포트와 어댑터 아키텍처라고도 불린다

유스케이스: 도메인 엔티티와 상호작용

어댑터: 애플리케이션과 다른 시스템 간의 번역을 담당

- 주도하는 어댑터(왼쪽에 위치): 애플리케이션 코어를 호출

- 주도되는 어댑터(오른쪽에 위치): 애플리케이션 코어에 의해 호출

포트: 애플리케이션 코어와 어댑터들 간의 통신을 위해 애플리케이션 코어가 제공하는 인터페이스

- 입력 포트: 코어에 있는 유스케이스에서 구현되고 주도하는 어댑터에 의해 호출되는 인터페이스

- 출력 포트: 주도되는 어댑터에 의해 구현되고 코어에 의해 호출되는 인터페이스

 

바깥 계층: 어댑터로 구성

애플리케이션 계층: 포트와 유스케이스 구현체의 결합

내부 계층: 도메인 엔티티

유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?

의존성을 역전시켜 도메인 코드가 바깥쪽 코드에 의존하지 않게 함으로써 변경할 이유의 수를 줄일 수 있고, 변경할 이유가 적을수록 유지보수성은 더 좋아진다.

도메인 코드는 비즈니스 문제에 맞게 모델링될 수 있고, 영속성/UI 코드도 각 문제에 맞게 모델링 될 수 있다.

728x90
반응형