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로 풀어서 정리 필요

    • 변수간 의존성을 분석해서 문제를 풀어야 한다.

    • 다른 변수로부터 추론할 수 없는 주요 데이터 소스를 찾고 데이터 레이어에서 관리하게 만듦

UseCase 정의 예시

Repository Layer 설계

  • UseCase와 Repository 사이의 boundary 설계는 주관적

  • Repository Layer는 모델 관련 동작들 가운데에 정의

  • Repository Layer 동작 원칙

    • Operation 최소화 (사용하는 인터페이스들만 public하게 노출)

      • 예로 모든 getter/setter를 노출하면 일관되지 않은 데이터를 쉽게 생성 가능해진다.

    • 동작은 중립적, UseCase Layer에서 정의한 비즈니스 로직과 독립적

    • 각 Repository 동작은 소스간의 일관성 유지 필요

      • 한 번에 하나의 Repository 동작

      • 한 번에 여러개의 Data Source를 변경하는 경우에만 atomic operation으로 여러 data source 접근

Repository interface 설계 예시

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를 적용 등의 처리를 적용

Presentation Layer 설계 예시

Data Layer

  • 애플리케이션이 데이터를 관리하는 방법

Data Layer 설계

  • Data Layer는 두개의 Sub Layer 존재

  • Data:Repository Layer

    • Domain의 Repository Layer에 정의된 동작 구현

  • Data:DataSource Layer

    • 실제 데이터 스토리지 구현

Data 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