‘만들면서 배우는 클린 아키텍처’ 기술 서적을 읽고 학습 내용을 정리하기 위한 목적의 TIL 포스팅입니다🙆♂️ 예제코드는 깃허브 레포지토리를 참고해주세요.
2장 - 의존성 역전하기
이번장에선 계층형 아키텍처 문제점에 대한 대안을 이야기한다.
단일 책임 원칙
- 하나의 컴포넌트는 오로지 한 가지 일만 해야 하고, 그것을 올바르게 수행해야 한다.(X)
- 좋은 조언이지만 SRP 의 실제의도는 아니다
- 컴포넌트를 변경하는 이유는 오직 하나뿐이어야 한다.(O)
- 책임은 1가지 일만 하는 것 보다 변경할 이유로 해석해야 한다.
- 단일변경 이유 원칙(Single Reson to Change Principle) 이 적절할 수도 있다.
- 변경 이유가 1개일때 1가지 일만 하는 것은 자동으로 따라온다.
note: 컴포넌트를 변경할 이유가 1가지라면 어떤 다른 이유로 소프트웨어를 변경하더라도 이 컴포넌트에 대해서는 전혀 신경 쓸 필요가 없다. 소프트웨어가 변경되더라도 여전히 우리가 기대한 대로 동작할 것이다.
- 하지만 변경할 이유는 컴포넌트 의존성을 통해 너무 쉽게 전파된다.
출처: https://jandari91.tistory.com/51
- 컴포넌트 E 의 경우 의존하는 것이 없으므로 변경할 유일한 이유가 E의 기능이 바뀔때 뿐이다
- 컴포넌트 A 의 경우 모든 컴포넌트를 의존하므로 어떤 컴포넌트가 바뀌어도 같이 바뀌어야 한다.
- SRP 위반시 변경하기가 더 어려워진다. 다른 컴포넌트의 실패요인으로 작용될 수 있다.
부수효과에 관한 이야기
- 저자는 클라이언트로 하여금 잘못 구조화된 소프트웨어를 변경하는데 더 많은 비용을 지불하도록 만드는 경우가 있었다.
의존성 역전 원칙
note: 코드상의 어떤 의존성이든 그 방향을 바꿀 수(역전시킬 수) 있다.
- 사실 의존성 양쪽 코들르 모두 제어할 수 있을때만 의존성을 역전시킬 수 있따. 만약 서드파티 라이브러리에 의존성이 있다면 의존성을 역전시킬 수 없다.
출처: https://jandari91.tistory.com/51
- 계층형 아키텍처에서 계층간 의존성은 항상 다음 계층인 아래 방향을 가리킨다. 단일 책임 원칙을 고수준에서 적용할때 상취 계층들이 하위 계층들에 비해 변경할 이유가 더 많다는것을 알 수 있다.
- 그러므로 영속성 계층에 대한 도메인 계층의 의존성 때문에 영속성 계층을 변경할때마다 잠재적으로 도메인 계층도 변경해야 한다. 그러나 도메인 코드는 애플리케이션에서 가장 중요한 영역이므로 영속성 코드가 바뀐다해서 도메인 코드까지 바꾸고 싶진 않다.
- 이 의존성을 어떻게 제거할 수 있을까? DIP를 적용(도메인 코드와 영속성 코드 간의 의존성을 역전시켜 도메인 코드를 ‘변경할 이유’의 개수를 줄임으로써)함으로써 가능하다.
- 엔티티를 도메인 계층으로 올리고 도메인 계층에 리포지토리 인터페이스를 만들고, 실제 리포지토리 구현체를 영속성 계층에서 구현(DIP 실제 적용)하게 하면 된다.
출처: https://jandari91.tistory.com/51
- 이 묘수로 영속성 코드에 있는 숨막히는 의존성으로부터 도메인 로직을 해방시켰다.
클린 아키텍처
- 로버트 마틴은 ‘클린 아키텍처’라는 용어를 같은 이름의 책에서 정립했다.
- 그는 클린 아키텍처에서는 설계가 비즈니스 규칙의 테스트를 용이하게 하고, 비즈니스 규칙은 프레임워크, DB, UI기술, 그 밖의 외부 애플맄네이션이나 인터페이스로부터 독립적일수있다고 이야기했다.
- 이는 도메인 코드가 바깥으로 향하는 어떤 의존성도 없어야함을 의미한다. 대신 의존성 역전 원칙의 도움으로 모든 의존성이 안쪽(도메인 코드)을 향하고 있다.
출처: https://jandari91.tistory.com/51
- 이 아키텍처에서 코어에는 주변 유스케이스에서 접근하는 도메인 엔티티들이 있다. 유스케이스는 앞에서 서비스라 불렀던것들인데, 단일 책임(즉, 변경할 단 한 가지의 이유)을 갖기 위해 조금 더 세분화돼 있다. 이를 통해 이전에 이야기했던
넓은 서비스 문제
를 피할 수 있다. - 도메인 코드에서는 어떤 영속성 프레임워크나 UI 프레임워크가 사용되는지 알 수 없기 떄문에 특정 프레임워크에 특화된 코드를 가질 수 없고 비즈니스 규칙에 집중할 수 있다.
- 그래서 도메인 코드를 자유롭게 모델링할 수 있다.
- 예를 들어, 도메인 주도 설계(DDD)를 가장 순수한 형태로 적용해볼수도 있다.
- 영속성이나 UI에 특화된 문제를 신경쓰지 않아도 된다면 이렇게 하기 굉장히 수월해진다.
- 하지만 클린아키텍처에선 대가가 따른다. 도메인 계층이 영속성이나 UI 같은 외부 계층과 철저히 분리돼야 하므로 애플리케이션의 엔티티에 대한 모델을 각 계층에서 유지보수해야 한다.
- 가령 영속성 계층에서 ORM프레임워크를 사용한다 했을떄 도메인 계층은 영속성 계층을 모르기 때문에 도메인 계층에서 사용한 엔티티 클래스를 영속성 계층에서 함께 사용할 수 없고 두 계층에서 각각 엔티티를 만들어 관리해야 한다. 즉, 도메인 계층과 영속성 계층이 데이터를 주고 받을때, 두 엔티티를 서로 변환해야 한다는 뜻이다.
- 이는 도메인 계층과 다른 계층들 사이에서도 마찬가지다.
- 실제 개인적인 경험으로 매번 컨버팅 작업을 해줘야하는 번거로운 작업임에도 불구하고 이는 바람직한 일이다. 이것이 바로 도메인 코드를 프레임워크에 특화된 문제로부터 해방시키고자 했던, 결합이 제거된 상태다.
- 가령 JPA 에선 ORM이 관리하는 엔티티에 인자가 없는 기본 생성자를 추가하도록 강제하는데 이것이 도메인 모델에는 포함해서는 안될 프레임워크에 특화된 결합의 예이다.
- 8장에선 도메인 계층과 영속성 계층의 결합을 그대로 수용하는 ‘매핑하지 않기’ 전략을 비롯한 여러 매핑 전략에 대해 살펴보겠다.
- 로버트 마틴의 클린 아키텍처는 다소 추상적이기에 조금 더 깊게 들어가서 클린 아키텍처의 원칙들을 조금 더 구체적으로 만들어주는 ‘육각형 아키텍처’에 대해 살펴보자.
육각형 아키텍처(헥사고날 아키텍처)
출처: https://jandari91.tistory.com/51
- 알리스테어 콕번이 만든 용어로 애플리케이션 코어가 육각형으로 표현되다보니 붙여진 이름이다.
- 육각형에서 외부로 향하는 의존성이 없기 때문에 마틴이 클린 아키텍처에서 제시한 의존성 규칙이 그대로 적용된다는 점을 주목하자. 대신 모든 의존성은 코어를 향한다.
- 왼쪽에 있는 어댑터들은 (애플리케이션 코어를 호출하기 때문에) 애플리케이션을 주도하는 어댑터들이고, 반면 오른쪽 어댑터들은 (애플리케이션 코어에 의해 호출되기 때문에) 애플리케이션에 의해 주도되는 어댑터들이다.
- 애플리케이션 코어와 어댑터들간의 통신이 가능하려면 애플리케이션 코어가 각각의 포트를 제공해야 한다. 주도하는 어댑터(driving adapter)에게는 그러한 포트가 코어에 있는 유스케이스 클래스 중 하나에 의해 구현되고 어댑터에 의해 호출되는 인터페이스가 될것이고, 주도되는 어댑터(driven adapter)에게는 그러한 포트가 어댑터에 의해 구현되고 코어에 의해 호출되는 인터페이스가 될 것이다.
- 이러한 핵심 개념으로 ‘포트와 어댑터’ 아키텍처로도 알려져 있다.
유지보수 가능한 소프트웨어를 만드는데 어떻게 도움이 될까?
- 의존성을 역전시켜 도메인 코드가 다른 바깥쪽 코드에 의존하지 않게 함으로써 영속성과 UI에 특화된 모든 문제로부터 도메인 로직의 결합을 제거하고 코드를 변경할 이유의 수를 줄일 수 있다. 그리고 변경할 이유가 적을수록 유지보수성은 좋아진다.
- 또한 도메인 코드는 비즈니스 문제 딱 맞도록 자유롭게 모델링 가능해지고, 영속성 코드와 UI코드도 영속성 문제와 UI문제에 맞게 자유롭게 모델링될 수 있다.