단일 책임 원칙 (Single Responsibility Principle, SRP)
컴포넌트를 변경하는 이유는 오직 하나뿐이어야 한다.
만약 컴포넌트를 변경할 이유가 한 가지라면 우리가 어떤 다른 이유로 소프트웨어를 변경하더라도 이 컴포넌트에 대해서는 전혀 신경 쓸 필요가 없다. 변경할 이유라는 것은 컴포넌트 간의 의존성을 통해 너무 쉽게 전파된다.
점선 화살표처럼 전이 의존성이라고 하더라도 어떤 컴포넌트의 의존성은 각각 이 컴포넌트를 변경하는 이유 하나씩에 해당한다.
컴포넌트 A는 다른 여러 컴포넌트에 의존하는 반면 컴포넌트 E는 의존하는 것이 없다.
컴포넌트 E를 변경할 유일한 이유는 새로운 요구사항에 의해 E의 기능을 바꿔야 할 때뿐이다. 반면 컴포넌트 A의 경우에는 모든 컴포넌트에 의존하고 있기 때문에 다른 어떤 컴포넌트가 바뀌든지 같이 바뀌어야 한다.
시간이 지날수록 변경하기가 더 어려워지고 변경 비용도 증가한다. 변경할 이유가 많이 쌓여가고 그렇게 되면 한 컴포넌트를 바꾸는 것이 다른 컴포넌트가 실패하는 원인으로 작용할 수 있다.
부수효과에 관한 이야기
과거에 핵심적인 특정 컴포넌트를 변경했을 때 다른 무언가가 망가지는 부수효과를 경험한 클라이언트는 조금 더 사용자 친화적이면서도 변경 비용이 저렴한 방식이 존재하더라도 핵심 컴포넌트를 변경하지 않는 복잡한 방식으로 구현되길 바랄 수 있다.
의존성 역전 원칙 (Dependency Inversion Principle, DIP)
계층형 아키텍처에서 계층 간 의존성은 항상 다음 계층인 아래 방향을 가리킨다. 단일 책임 원칙을 고수준에서 적용할 때 상위 계층들이 하위 계층들에 비해 변경할 이유가 더 많다는 것을 알 수 있다.
영속성 계층에 대한 도메인 계층의 의존성 때문에 영속성 계층을 변경할 때마다 잠재적으로 도메인 계층도 변경해야 한다.
코드상의 어떤 의존성이든 그 방향을 역전시킬 수 있다.
양쪽 코드를 모두 제어할 수 있을 때만 의존성을 역전시킬 수 있다. 서드파티 라이브러리에 의존성이 있다면 해당 라이브러리를 제어할 수 없기 때문에 이 의존성은 역전시킬 수 없다.
도메인 계층에 리포지토리에 대한 인터페이스를 만들고, 실제 리포지토리는 영속성 계층에서 구현해 의존성을 역전시킴으로 도메인 코드를 변경할 이유의 개수를 줄일 수 있다.
클린 아키텍처
설계가 비즈니스 규칙의 테스트를 용이하게 하고,
비즈니스 규칙은 프레임워크, 데이터베이스, UI 기술, 그 밖의 외부 애플리케이션이나 인터페이스로부터 독립적일 수 있다.
도메인 코드가 바깥으로 향하는 어떤 의존성도 없어야 함을 의미한다. 의존성 역전 원칙의 도움으로 모든 의존성이 도메인 코드를 향하고 있다.
이 아키텍처의 코어에는 주변 유스케이스에서 접근하는 도메인 엔티티들이 있다. 유스케이스는 서비스라고 불리는 것들로 단일 책임을 갖기 위해 세분화되어있다. 이를 통해 넓은 서비스 문제를 피할 수 있다.
코어 주변으로 비즈니스 규칙을 지원(UI, 영속성 등 제공)하는 애플리케이션의 다른 모든 컴포넌트들을 확인할 수 있다. 바깥쪽 계층들은 다른 서드파티 컴포넌트에 어댑터를 제공할 수 있다.
도메인 코드에서는 어떤 영속성 프레임워크가 사용되는 지 알 수 없기 때문에 특정 프레임워크에 특화된 코드를 가질 수 없고 비즈니스 규칙에 집중할 수 있다. 그래서 도메인 코드를 자유롭게 모델링할 수 있다.
애플리케이션의 엔티티에 대한 모델을 각 계층에서 유지보수 해야 한다.
영속성 계층에서 ORM 프레임워크를 사용하는 경우 데이터베이스 구조 및 컬럼의 매핑을 서술한 메타데이터를 담고 있는 엔티티 클래스를 필요로 한다. 도메인 계층은 영속성 계층을 모르기 때문에 도메인 계층에서 사용한 엔티티 클래스를 영속성 계층에서 함께 사용할 수 없고 두 계층에서 각각 엔티티를 만들어야 하며 계층 간에 데이터를 주고받을 때 두 엔티티를 변환해야 한다. 이는 바람직한 일이다.
육각형 아키텍처(헥사고날 아키텍처)
유스케이스: 도메인 엔티티와 상호작용
어댑터: 애플리케이션과 다른 시스템 간의 번역을 담당
- 주도하는 어댑터(왼쪽에 위치): 애플리케이션 코어를 호출
- 주도되는 어댑터(오른쪽에 위치): 애플리케이션 코어에 의해 호출
포트: 애플리케이션 코어와 어댑터들 간의 통신을 위해 애플리케이션 코어가 제공하는 인터페이스
- 입력 포트: 코어에 있는 유스케이스에서 구현되고 주도하는 어댑터에 의해 호출되는 인터페이스
- 출력 포트: 주도되는 어댑터에 의해 구현되고 코어에 의해 호출되는 인터페이스
바깥 계층: 어댑터로 구성
애플리케이션 계층: 포트와 유스케이스 구현체의 결합
내부 계층: 도메인 엔티티
유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?
의존성을 역전시켜 도메인 코드가 바깥쪽 코드에 의존하지 않게 함으로써 변경할 이유의 수를 줄일 수 있고, 변경할 이유가 적을수록 유지보수성은 더 좋아진다.
도메인 코드는 비즈니스 문제에 맞게 모델링될 수 있고, 영속성/UI 코드도 각 문제에 맞게 모델링 될 수 있다.
'📙 > Study' 카테고리의 다른 글
[만들면서 배우는 클린 아키텍처] 04. 유스케이스 구현하기 (0) | 2024.10.07 |
---|---|
[만들면서 배우는 클린 아키텍처] 03. 코드 구성하기 (2) | 2024.10.06 |
[만들면서 배우는 클린 아키텍처] 01. 계층형 아키텍처의 문제는 무엇일까? (3) | 2024.10.04 |
[Effective Java 3/E] Item 5. 의존 객체 주입 (0) | 2021.01.10 |
[Effective Java 3/E] Item 4. private 생성자 (0) | 2021.01.10 |