[TIL] 만들면서 배우는 클린 아키텍처 2장 ~3장

단일책임원칙

일반적으로 알고 있는 것과 달리, “컴포넌트를 변경하는 이유는 오직 하나뿐이어야 한다”라고 정의하는 것이 더 명쾌하다.

  • 컴포넌트를 변경할 이유가 오직 하나뿐이라면 다른 이유로 소프트웨어를 변경하더라도 해당 컴포넌트를 신경 쓸 필요가 없다.

의존성 역전 원칙

  • 도메인 코드와 영속성 코드 간 의존성을 역전시켜 영속성 코드가 도메인 코드에 의존하고, 도메인 코드를 변경할 이유를 최소화한다.
  • 도메인 계층에 리포지토리에 대한 인터페이스를 만들고, 실제 리포지토리는 영속성 계층에 구현한다.
  • 모든 의존성이 도메인 코드를 향하지만, 각 계층마다 entity를 만들어야 한다.

헥사고날 아키텍처

어댑터와 포트, 애플리케이션 코어로 이루어진 아키텍처.

  • 애플리케이션 코어를 호출하는 Driving Adapters와 애플리케이션 코어에 의해 호출되는 Driven Adapters로 구성

계층으로 구성하기

buckpal
- domain
  - Account
  - Activity
  - AccountRepository
  - AccountService
- persistence
  - AccountRepositoryImpl
- web
  - AccountController

처음 헥사고날 아키텍처를 적용하려다 보면 위와 같은 구조가 되기 십상이다.

장점

  • 의존성 역전 원칙을 활용해 의존성이 domain 패키지 내 코드만을 향함

단점

  • 서로 연관되지 않은 기능들끼리 묶여있어 예상치 못한 부수효과를 일으킬 수 있다.
  • 애플리케이션이 어떤 유스케이스를 제공하는지 파악할 수 없다.
  • 인커밍 포트와 아웃고잉 포트가 코드 속에 숨겨져 있어 어떤 아키텍처인지 파악하기 어렵다.

기능으로 구성하기

buckpal
- account
  - Account
  - AccountController
  - AccountRepository
  - AccountRepositoryImpl
  - SendMoneyService

다음으로 기능을 중심으로 구조화 하는 것도 고려하게 된다.

장점

  • 패키지 경계를 package-private 접근 수준과 결합하면 각 기능 사이의 불필요한 의존성을 방지할 수 있다.

단점

  • 어댑터를 나타내는 패키지명이 부재하고, 인커밍 포트, 아웃고잉 포트를 확인할 수 없어 패키지 가시성이 크게 떨어진다.
  • 도메인 코드와 영속성 코드 간 의존성 역전이 되었으나, package-private 접근 수준에서 도메인 코드가 실수로 영속성 코드에 의존하는 것을 방지할 수 없다.

바람직한 구조

마지막으로 아키텍처적으로 표현력 있는 패키지 구조를 고민해본다.

buckpal
- account
  - adapter
    - in
      - web
        - AccountController
    - out
      - persistence
        - AccountPersistenceAdapter
        - SpringDataAccountRepository
    - domain
      - Account
      - Activity
    - application
      - SemdMoneyService
      - port
        - in
          - SendMoneyUseCase
        - out
          - LoadAccountPort
          - UpdateAccountStatePort

구조에서 port는 인터페이스이고 adapter는 구현체다.

장점

  • 아키텍처-코드 갭이 최소화된다. 아키텍처에 코드가 효과적으로 매핑되어 가시성이 좋고 협업이 쉽다.
  • 도메인에서 기능 간 패키지 분리를 통해 불필요한 기능간 의존성 발생을 방지했다.
  • DDD 개념에 직접 대응시킬 수 있다.

특징

  • 어댑터 패키지들은 application의 포트 인터페이스를 통해서만 호출되므로 package-private하게 유지할 수 있다. 따라서 애플리케이션 계층에서 어댑터 클래스로 향하는 우발적 의존성이 발생하지 않는다.
  • application 패키지와 domain 패키지의 일부 클래스는 public이어야 한다.
  • 서비스는 인커밍 포트 인터페이스에 숨겨질 수 있으므로 public일 필요가 없다.

의존성 주입의 필요성

  • 도메인이 아웃고잉 어댑터에 의존성을 갖지 않기 위해서는 의존성 주입을 통해 의존성을 역전시킬 필요가 있다.