만들면서 배우는 클린 아키텍쳐
계층형 아키텍처의 문제점
웹 → 도메인 → 영속성 으로 구성된 전통적인 웹 애플리케이션 구조
웹 계층에서 요청을 받아 도메인 or 비지니스 계층의 서비스로 요청을 보내면
서비스에서 필요한 비지니스 로직을 수행하고
도메인 엔티티의 현재 상태를 조회하거나, 변경하기 위해 영속성 계층의 컴포넌트를 호출한다
계층형 아키텍쳐의 단점
계층형 아키텍쳐는 데이터베이스 주도 설계를 유도하게 된다.
점점 하위 레이어에 의존하기 때문에 결국 영속성 계층인 데이터베이스의 상태에 의존하게 된다.
만약 도메인 계층과 영속성 계층 사이에 강한 결합이 생기면, 서비스가 영속성 모델을 비지니스 모델 처럼 사용하게 되고, 이로 인해 영속성 관련 작업들(eager, lazy loading, 트랜잭션, 캐시 flush…)을 서비스에서 해야된다.
개발에 편리하다는 이유로 특정 컴포넌트들이(헬퍼, 유틸리티 등) 영속성 계층에 쌓이는 경우가 생긴다
웹 계층에 도메인 계층을 건너뛰어서 영속성계층에 바로 접근하는 코드가 생긴다면 도메인 로직이 점점 흩어진다.
이 경우 웹 계층 테스트를 하는데 영속성 계층도 모킹해서 테스트해야되서, 유닛 테스트의 복잡도가 올라간다. 이렇게 되면 종속성을 이해하고 mocking을 하는데 많은 시간이 소요되서 테스트 자체를 회피하게 된다.
유스케이스를 숨기게 된다.
모든 계층에 유스케이스가 흩어져서 기능 추가나 변경을 위한 코드를 탐색하기 힘들어진다.
하나의 서비스가 여러 개의 유스케이스를 담당하는 등 아주 큰 서비스가 만들어진다.
넓은 서비스는 영속성 계층에 많은 의존성을 가지게 되고, 웹 계층의 많은 컴포넌트가 이 서비스에 의존하게 된다. 그렇다는 것은 서비스를 테스트하기 힘들어지고, 특정 기능을 리팩토링할 때 그 기능에 해당하는 코드를 찾기 힘들어진다.
⇒ UserService 에서 사용자 등록 유스케이스를 찾는 대신 RegisterUserService 가 있다면 그 서비스클래스만 바로 열어서 작업할 수 있다.
동시 작업이 어려워진다.
레이어드 아키텍쳐 상에서는 모든 것이 영속성 계층 위에 만들어지기 때문에 계층별로 순차적으로 개발을 해야되서 하나의 기능을 한 명이 개발자만 작업할 수 있다.
인터페이스를 미리 정의해 놓는다고 해도, 데이터베이스 주도 설계는 영속성 로직이 도메인 로직과 너무 뒤섞여서 각각을 개별적으로 작업하기가 힘들다.
코드에 넓은 범위의 서비스 클래스가 있다면 서로 다른 기능을 동시에 작업하기가 어렵다.
애플리케이션은 비지니스를 관장하는 규칙, 정책에 기반한 모델(도메인 로직)을 만들어야 편리하게 사용할 수 있다. state가 아닌 behavior를 중심으로 모델링하는 것이 더 유연할 수 있다.
이 경우 도메인 로직을 먼저 만들고, 이 로직이 맞는 것을 확인한 뒤에 영속성 계층과 웹 계층을 만들어야한다.
의존성 역전하기
객체지향의 SOLID 원칙 중 S, D를 적극적으로 지향
SRP - 단일 책임 원칙
하나의 컴포넌트는 한 가지 일만 해야한다컴포넌트를 변경하는 이유는 오직 하나 뿐이여야만 한다
만약 컴포넌트를 변경하는 이유가 단 한 가지 라면 우리가 다른 이유로 소프트웨어를 변경할 때 이 컴퓨넌트에 대해서는 신경쓸 필요가 없어진다.
현실적으로 변경할 이유는 컴포넌트 간 의존성을 통해 쉽게 전파된다.
단일 책임 원칙을 위반한 컴포넌트는 다른 컴포넌트에 의존하기 떄문에 다른 컴포넌트 변경 시 같이 변경해야되서 비용이 증가한다.
부수효과
잘 설계된 컴포넌트는 기능을 새로 구현하거나 변경시에 부수효과가 없어진다.
DIP - 의존성 역전 원칙
레이어드 아키텍쳐에서 계층 간 의존성은 항상 다음 계층인 아래 방향을 가리킨다.
이 경우 고수준의 컴포넌트에 SRP를 적용하면 저수준의 컴포넌트에 비해 변경할 이유가 더 많아진다.
영속성 계층에 대한 도메인 계층의 의존성 때문에 영속성 계층을 변경할 때마다 도메인 계층도 변경해야한다.
코드상의 어떠한 의존성이든 그 방향을 바꿀 수 있다(역전시킬 수 있다)
양측 컴포넌트를 모두 제어할 수 있을 때만 의존성을 역전시킬 수 있다.
만약 서드파티 라이브러리에 의존성이 있다면, 해당 라이브러리를 제어할 수 없기 때문에 이 의존성은 역전시킬 수 없다
영속성 계층의 레포지토리가 도메인 계층에 있는 엔티티에 의존하게 만들 수 있다.
도메인코드와 영속성 코드 간의 의존성을 역전시켜 영속성 코드가 도메인 코드에 의존하고, 도메인 코드의 변경할 이유를 줄여보자
영속성 계층의 리포지토리가 도메인 계층에 있는 엔티티에 의존하도록 만든 뒤에 도메인 계층에 리포지토리에 대한 인터페이스를 만들고, 실제 리포지토리는 영속성 계층에서 구현하게 하면 된다.
ㅑ
클린 아키텍쳐
애플리케이션이 다른 시스템이나 어댑터에 연결되는 4개 이상의 면(port)을 가질 수 있음을 보여주려고 육각형이라고 이름 붙였다고 함
육각형 안에는 도메인 엔티티와 상호작용하는 유스케이스가 있음. 모든 의존성은 코어를 향한다
어댑터 - 포트 - 유즈케이스 - 엔티티
클린 아키텍쳐에서 모든 의존성은 도메인 로직을 향해 안으로 향한다.
코어는 주변 유스케이스에서 접근하는 도메인 엔티티
유스케이스(단일 책임으로 잘게 분리된 서비스)
비지니스 규칙을 지원하는 나머지 컴포넌트(영속성, UI, 서드파티 컴포넌트에 대한 어댑터…)
클린 아키텍쳐를 사용했을 때의 대가
도메인 계층이 영속성, UI 등의 외부 계층과 철저히 분리해야한다는 것은 애플리케이션 엔티티에 대한 모델을 각 계층에서 유지보수해야한다.
헥사고날 아키텍쳐
애플리케이션이 다른 시스템이나 어댑터에 연결되는 4개 이상의 면(port)을 가질 수 있음을 보여주려고 육각형이라고 이름 붙였다고 함
육각형 안에는 도메인 엔티티와 상호작용하는 유스케이스가 있음. 모든 의존성은 코어를 향한다
어댑터 - 포트 - 유즈케이스 - 엔티티
Last updated