Clean Architecture
클린 아키텍처란?
로버트 C 마틴이 제시한 개념
소프트웨어 시스템 아키텍처 디자인 패턴 중 하나
소프트웨어 시스템을 설계하고 구성하는 방법
소프트웨어 시스템을 깔끔하게 유지하고 확장 가능하며 유지보수성을 높힘
아키텍처 특징
프레임워크 독립성
테스트 용이성
UI 독립성
데이터베이스 독립성
모든 외부 에이전시에 대한 독립성
가장 중요한 규칙 (의존성)
안쪽에 있을수록 고수준의 정책을 의미함
고수준의 정책은 바깥쪽을 의존하지 않게
의존성은 반드시 안쪽으로 향함
주요 구성 요소
엔티티
비즈니스 규칙을 담고 있는 엔티티, 가장 안쪽에 위치하며 가장 안정적인 부분
핵심 업무 규칙
외부의 변경으로부터 영향을 받지 않는 영역
유스케이스
비즈니스 규칙을 적용하고 시스템의 행위를 제어하는 부분
어플리케이션에 특화된 업무 규칙을 포함
인터페이스 어댑터
데이터를 외부와 주고받을 때 사용하는 부분
컨트롤러, 프레젠터, 게이트웨이
프레임워크와 드라이버
외부 도구, 프레임워크, 라이브러리 등과의 연결을 담당
프레임워크, 데이터베이스, 웹 서버 등
장점
1. 유지보수성 향상
각 구성 요소가 독립적으로 존재, 각각의 책임이 분명하게 정의
코드를 변경하거나 유지보수하는데 용이
변경이 필요한 부분을 신속하게 식별하고 수정
2. 확장성 및 변동성 제어
안쪽의 원은 안정적이고 변동성이 낮은 엔티티
시스템의 핵심 비즈니스 규칙은 안정적으로 유지
바깥쪽의 원은 변동성이 큰 부분
외부 요인에 대한 대응이 유연
3. 테스트 용이성
각 구성 요소는 독립적으로 테스트 가능
의존성이 명확하게 정의되어 있어 모킹등을 통한 테스트가 용이
4. 프레임워크와 도구의 유연한 교체
외부 프레임워크, 라이브러리, 디비 등과의 의존성이 인터페이스 어댑터를 통한 추상화
필요에 따라 이들을 교체하거나 업그레이드하는데 용이
5. 개발자와 비즈니스 로직의 분리
핵심 비즈니스 로직이 안쪽의 원에 위치, 엔티티
프레임워크나 인터페이스와 독립적으로 작성
개발자가 핵심 비즈니스 로직에 집중할 수 있음
6. 플랫폼 독립성
안쪽의 원에는 플래폼에 종속적인 부분이 없음 -> 엔티티
바깥쪽의 원에서 의존성, 종속성을 처리
플랫폼 간의 이식성이 향상
7. 비즈니스 규칙의 명시적 표현
비즈니스 규칙이 명시적으로 드러나도록 하는 구조를 제공
비즈니스 요구사항을 반영하도록 하며, 코드를 이해하고 관리하는데 돕는다.
8. 도메인 주도 설계(Domain-Driven Design)와의 조합
도메인 주도 설계 원칙과 잘 조화
비즈니스 규칙을 중심으로 시스템을 구성하고 확장 가능
설계
Clean Architecture에 따르는 구조화
Domain Layer
애플리케이션이 수행하는 작업을 설명
플랫폼/프레임워크와 독립적으로 구성
Model -> 문제와 관련된 실제 세계의 Object
Repository ->
Model
에 접근 가능한 인터페이스 제공UseCase -> 애플리케이션의 비즈니스 로직 포함
Model Layer 설계
데이터 모델 추출
비즈니스 규칙에 초점을 맞춘 필요한 데이터를 정의 (프레임워크, 플랫폼에 구애받지 않음)
Typescript를 이용해서 정의
UseCase Layer 설계
"A가 발생하면 B를 수행한다." 로 정의
React에서의 UseCase
React Application에 의해 호출되는 렌더링 함수
사용자 입력을 위한 이벤트 핸들러
자발적으로 발생되는 effect
대부분 컴포넌트 내부에서 이런 UseCase들이 스파게티처럼 뒤섞여 정의되게 된다.
적절한 Layer로 풀어서 정리 필요
변수간 의존성을 분석해서 문제를 풀어야 한다.
다른 변수로부터 추론할 수 없는 주요 데이터 소스를 찾고 데이터 레이어에서 관리하게 만듦
Repository Layer 설계
UseCase와 Repository 사이의 boundary 설계는 주관적
Repository Layer는 모델 관련 동작들 가운데에 정의
Repository Layer 동작 원칙
Operation 최소화 (사용하는 인터페이스들만 public하게 노출)
예로 모든 getter/setter를 노출하면 일관되지 않은 데이터를 쉽게 생성 가능해진다.
동작은 중립적,
UseCase Layer
에서 정의한 비즈니스 로직과 독립적각 Repository 동작은 소스간의 일관성 유지 필요
한 번에 하나의 Repository 동작
한 번에 여러개의 Data Source를 변경하는 경우에만 atomic operation으로 여러 data source 접근
Presentation Layer
애플리케이션이 실제 세계와 상호작용하는 방식을 설명
즉, 사용자에게 화면으로 보여지는 레이어
리엑트 컴포넌트에서 사용자에게 어떤 UI를 어떻게 렌더링할지에 대해 초점을 둔다.
Presentation Layer 설계
MVC 형태를 구성하는것이 중요 (Moel, View, Controller)
Model과 Controller를 하나의 객체로 병합
Presentation Layer와 UseCase Layer 사이의 브릿지로 사용
컴포넌트 렌더 부분 (View), 커스텀 훅으로 (Model - Controller) 참조함으로써 View와 Model-Controller 분리
View에서는 커스텀훅(Model-Controller)를 통해서 상위 레이어에 있는 동작들(데이터소스)에 접근하거나 UseCase를 적용 등의 처리를 적용
Data Layer
애플리케이션이 데이터를 관리하는 방법
Data Layer 설계
Data Layer는 두개의 Sub Layer 존재
Data:Repository Layer
Domain의 Repository Layer에 정의된 동작 구현
Data:DataSource Layer
실제 데이터 스토리지 구현
Main Layer
모든 구성요소를 묶어 하나의 애플리케이션으로 결합
Main Layer 설계
여러 레이어를 하나의 애플리케이션으로
Repository 구현을 생성하고 View에 의존성 주입
Repository는 modelController(custom hook)를 통해 useCase Layer로 전달
UseCase는 Repository를 사용
UseCase에서 RepositoryImpl를 생성하면 안됨
객체 생성은 Main Layer에서, 객체 사용은 UseCase Layer에서로 분리
의존성 흐름의 위배
실제 애플리케이션은 제어 흐름이 항상 한 방향으로 흐르지 않는다.
UseCase의 비즈니스 로직이 Repository의 인터페이스를 사용
Repository에서 data layer에서 관리되는 데이터에 접근 등
이런 의존성 규칙 위반을 해결하기 위한 수단으로 의존성 역전을 사용
의존성 역전 (Dependency Injection)
interface - implement의 구조
인터페이스 설계에 기반으로 실제 구현체는 Data layer에서 정의
정의체는 상위 레이어에 있지만 구현체는 하위에 둠으로써 서로간 느슨한 결합을 통해서 의존성 역전을 구성할 수 있다.
의존성 파악 도구
코드의 의존성이 단방향으로 가리킬수 있는 의존성 규칙을 유지할 수 있는 도구(Lint와 같은)
소스파일을 분석, 파일간 의존성 검증하는 도구
rule을 검증
위반된 규칙을 텍스트/그래픽으로 표시
부수적으로 시각화된 다양한 출력 형식의 dependency graph를 생성
소스 코드 검증
Dependency graph 생성
Graphviz 사용으로 의존성 그래프 생성
script 추가
위 명령어들은 자주 사용되므로 스크립트에 등록
Clean Architecture를 위한 Rule Custom
일반 규칙으로 감지하지 못하는 상황 1. Presentation에서 Data Layer를 참조하는 상황 (순환 참조가 아니므로 일반규칙에선 감지 못함)
Presentation Layer 의존성이 Domain으로 향하게끔 rule 설정
But 다른 유효한 의존성도
not-in-allowed
로 표시된다.allowed
규칙을 사용하는 경우 dependency-cruiser는 적어도 하나 이상의 규칙을 충족하지 않는 각각의 의존성에 대해not-in-allowed
로 표시한다.유효한 의존성을 명시적으로 모두 작성해주어야 한다.
번거롭지만, 리포지토리에 새 폴더를 추가할 때 마다 의존성을 설계하고 새 규칙을 추가하는 것이 좋은 습관
큰 Repo의 의존성 시각화
상위 수준(폴더 기준)의 그래프 확인하여 wide하게 확인할 수 있다.
의존성 검증을 git과 연동
여러 멤버가 같은 리포지토리에서 작업할 때 클린 아키텍처 의존성을 유지하기 어렵다.
변경사항을 적용*(커밋)할 때마다 의존성 크롤러를 실행해서 검증 (Husky)
Last updated