sungwony

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

development/architecture

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

일상이상삼상 2022. 10. 1. 00:16

대안은 무엇인가?

SRP(단일 책임 원칙)와 DIP(의존성 역전 원칙) 부터 시작해보자

단일 책임 원칙


일반적 해석

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

실제적 정의

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

아키텍처에서는?

  • 컴포넌트를 변경할 이유가 한 가지라면 어떤 다른 이유로 소프트웨어를 변경하더라도 이 컴포넌트에 대해서는 전혀 신경 쓸 필요가 없다는 것
  • 이것은 의존성에 의해 너무도 쉽게 깨진다
 

부수효과에 관한 이야기


컴포넌트의 부수효과로 인해 소프트웨어를 변경하는데 더 많은 비용이 지불된다

 

의존성 역전 원칙


계층 간 의존성은 항상 다음 계층인 아래 방향

영속성을 변경 → 도메인도 함께 변경된다

의존성을 어떻게 제거할 수 있을까?

의존성 역전 원칙

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

영속성 코드가 도메인 코드에 의존하게 만들어보자

 

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

영속성 코드가 도메인 코드에 의존?

→ 의존성 방향상 Repository 인터페이스의 변경에 의해 RepositoryImpl의 변경이 발생하므로 의존적임

코드로 풀면 이런 코드가 될 것 같은데 Repository 인터페이스의 변경이 Service 변경을 일으키면 똑같은거 같기도..

(하지만 오너십이 Persistence에서 Domain으로 바뀐것이 상당히 크다)

class Service {
  private Repository repository;
  
  public Entity get() {
    return repository.get();
  }
}

class RepositoryImpl implements Repository {
  public Entity get() {
    //...
  }
}

interface Repository {
  public Entity get();
}

 

클린 아키텍처


  • 설계는 비즈니스 규칙의 테스트를 용이하게 한다
  • 비즈니스 규칙은 프레임워크, 데이터베이스, UI 기술, 외부 애플리케이션, 인터페이스로부터 독립적일 수 있다

즉, 도메인 코드가 바깥으로 향하는 어떤 의존성도 없어야 한다

모든 의존성은 도메인 로직을 향해 안쪽 방향으로 향한다
 
  • 아키텍처의 코어(core)에는 주변 유스케이스에서 접근하는 도메인 엔티티들이 있다
  • 유스케이스는 앞에서 서비스라고 불렀던 것들로 단일 책임을 갖기 위해 조금 더 세분화돼 있다
    • 넓은 서비스 문제를 피할 수 있다
  • 바깥쪽 계층들은 다른 서드파티 컴포넌트에 어댑터를 제공할 수 있다
  • 도메인 코드는 영속성 프레임워크, UI 프레임워크에 대한 정보가 없기 때문에 특정 프레임워크에 특화된 코드를 가질 수 없고 비즈니스 규칙에 집중할 수 있다
  • 대가 : 애플리케이션의 엔티티에 대한 모델을 각 계층에서 유지보수 해야 한다

→ 대가가 꽤 크다?!

 

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


육각형 아키텍처는 애플리케이션 코어가 각 어댑터와 상호작용하기 위해 특정 포트를 제공하기 때문에 ‘포트와 어댑터’ 아키텍처라고도 불린다
 
  • N각형은 특별한 의미가 없다
    • 애플리케이션이 다른 시스템이나 어댑터와 연결되는 4개 이상의 면을 가질 수 있음을 보여주기 위해 육각형을 채택
  • 육각형 내부
    • 도메인 엔티티와 상호작용하는 유스케이스가 존재
    • 육각형 외부로 향하는 의존성이 없다
  • 육각형 외부
    • 애플리케이션과 상호작용하는 다양한 어댑터들이 있다
    • 애플리케이션 코어와 어댑터들 간의 통신이 가능하려면 애플리케이션 코어가 각각의 포트를 제공해야 한다
    • 왼쪽 어댑터
      • 애플리케이션 코어를 호출
      • 애플리케이션을 주도
      • 포트가 코어에 있는 유스케이스 클래스들에 의해 구현되고 어댑터에 의해 호출됨
    • 오른쪽 어댑터
      • 애플리케이션 코어에 의해 호출
      • 애플리케이션이 주도
      • 포트가 어댑터에 의해 구현되고 코어에 의해 호출됨
  • 육각형 아키텍처를 클린 아키텍처같은 계층으로 구현
    • 바깥쪽 → 애플리케이션과 다른 시스템 간의 번역을 담당하는 어댑터 계층
    • 중간 → 포트와 유스케이스 구현체를 결합한 애플리케이션 계층
    • 안쪽 → 도메인 엔티티

 

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


의존성을 역전시켜 도메인 코드가 바깥쪽 코드에 의존하지 않는다

도메인 로직이 변경될 이유가 적어지므로 유지보수성이 올라간다

각 영역이 자신의 문제에 맞게 자유롭게 모델링 될 수 있다