Clean-Architecutre #5-1부 - 좋은 아키텍처란?

Clean-Architecutre #5-1부 - 좋은 아키텍처란?

좋은 아키텍처의 개요

15. 아키텍처란?

시스템을 만들어본 사람들이 만들어낸 시스템의 형태

시스템을 컴포넌트로 분할하는 방법, 분할된 컴포넌트를 배치하는 방법, 컴포넌트가 서로 의존성을 갖는 방식에 따라 정해진다.
중요점은 아키텍처에 따라 시스템이 개발, 배포, 운영, 유지보수 되는 방식과 난이도가 결정된다는 것이다.
구조를 이해하기 쉽게 만들고 쉽게 개발하고 쉽게 유지보수하고 쉽게 배포하게 하는게 아키텍처의 궁극적인 목표다.

가능한 많은 선택지를, 가능한 오래 남겨두는 전략을 따라야 한다.

🌟 아키텍처의 중요성

  • 개발
    • 아키텍처가 고려되지 않고 일정 완료 개발에 몰두하게 된다면 (아이젠하워의 3번째 긴급하지만 중요하지 않은 일)
    • 수명이 짧고 건강하지 못한 시스템이 만들어진다.
  • 배포
    • 시스템이 효용가치를 가지려면 배포하여 쓰여야 한다. 그런데 배포비용이 높을 수록 쓸모가 없어지는 것과 같다.
    • 아키텍처가 고려되지 않고 개발된 시스템은 배포가 어렵고, 배포가 어렵다면 쓸모가 없어진다.
  • 운영
    • 아키텍처가 운영에 미치는 영향은 개발, 배포, 유지보수에 비해 덜 극적이다.
    • 시스템 전반적으로 운영하는데 도움이 되는 도구나 방식을 컨트롤해줘 개발과 유지보수에 큰 도움이 된다.
  • 유지보수
    • 가장 비용이 많이드는 구간이기 때문에 주의를 기울여 아키텍처를 만들면 비용이 크게 줄 수 있다.
    • 시스템을 컴포넌트로 분리하고 안정된 인터페이스를 두고 격리한다. 확장에 유리하고 장애를 줄이는데 도움이 된다.

🌟 소프트웨어의 진정한 가치와 유지하는법

소프트웨어는 행위적 가치구조적 가치를 가지게 되는데, 부드러운 웨어기 때문에 구조적 가치가 더 중요하다.
구조적으로 부드러워야 변화에 유연하게 대처할 수 있기 때문이다.
부드럽게 유지하기 위해서는 가능한 많이, 오랫동안 세부사항에 대한 선택사항을 열어두는 것이다.

소프트웨어 시스템은 주요한 두 가지 구성요소로 분해 가능하다.

  • 정책
    • 업무 규칙과 업무 절차를 구체화 (실제 필요한 기능)
  • 세부사항
    • 사람, 외부 시스템, 프로그래머가 정책과 소통할 때 필요한 요소
    • ex) database 종류, 입출력 장치, 프레임워크, 서버 종류, 통신 프로토콜 등

예를 들어 Database가 어떤 것인지 개발 초기부터 결정할 필요 없다. 추상체로 어떤 db가 오던지 Plugin이 가능하게 구조를 짜면 된다.
세부사항에 의존하지 않은 핵심 정책을 만들면 더 원하는 정책을 유연하게 구현할 수 있다.
결국 우리의 핵심 기능이 장치나 도구에 독립적이어야 할 필요가 있다.

좋은 아키텍트는 세부사항을 정책으로부터 신중하게 가려내고, 정책이 세부사항과 결합되지 않도록 엄격하게 분리한다.
마치 내 Service나 Repository는 어떤 DB가 나를 쓸지 1도 모르게 만드는 것이다.


16. 독립성

  • 좋은 아키텍처란?
    • 시스템의 유스케이스 지원
    • 시스템의 운영
    • 시스템의 개발
    • 시스템의 배포

🌟 유스케이스

김현우_usecase예제

  • 유스케이스는 시스템이 제공하는 기능을 명세한다.
  • 예를 들어서 장바구니는 상품을 추가하고 삭제하고 조회하는 기능을 제공한다.
  • 행위를 명확하게 하고 외부(Controller, Persistence)에 드러낸다.
  • 시스템이 지닌 의도를 아키텍처 수준에서 알아볼 수 있게 만드는 것이다.

🌟 운영

시스템에서 필요되는 요구치는 상황별로 다르다.
예를 들어 초당 100,000의 고객을 처리해야하는 경우에는 시스템에 맞는 유스케이스와 처리량을 보장해줘야 한다.

그리고 가변적이다. 그래서 최대한 컴포넌트를 적절히 격리하여 유지, 방식에 제한을 두지 않아야 한다.(구현체 선택을 최대한 미룬다)

🌟 개발

각 컴포넌트는 독립적으로 개발되어야 한다.
관심사는 domain에 따라 분리되어야 한다.
컴포넌트 결합도와 응집도법칙에 따라 잘 나눠 최대한 서로 간섭 없이 독립적으로 존재해야 개발하기 편하다.

🌟 배포

시스템이 빌드된 후 즉각적인 배포를 할 수 있어야 좋은 아키텍처다.
즉각적인 배포를 위해서는 컴포넌트가 독립적으로 배포가 가능해야 한다.
컴포넌트가 독립적으로 배포가 가능하려면 컴포넌트가 독립적으로 개발되어야 한다.

🌟 선택사항 열어놓기

내 메인 어플리케이션이 외부의 변화(Infra, Client)로부터 보호되게 최대한 독립적으로 개발되어야 한다.

🌟 계층 결합 분리

bank
│
├─ adapter
│  ├─ in
│  │  ├─ web          (웹 요청을 처리하는 컨트롤러 및 DTO)
│  │  └─ ui           (사용자 인터페이스를 처리하는 컴포넌트나 클래스)
│  │
│  └─ out
│     └─ persistence  (데이터베이스와의 연동을 위한 리포지토리나 DAO)
│       └─jpa 
│           └─ jpaEntity  (jpa를 사용할경우 @Entity는 여기)
│           └─ mapper    ( domain-entity 변환 mapper는 여기)
│
├─ application
│  ├─ port
│  │  ├─ in           (내부에서 사용하는 API 정의)
│  │  └─ out          (외부 시스템, 예: 데이터베이스, 다른 서비스와의 연동을 위한 인터페이스 정의)
│  │
│  └─ service         (유스케이스를 구현하는 서비스 클래스)
│
└─ domain             (도메인 모델, 엔터티, 값 객체, 도메인 서비스, 도메인 이벤트 등)

  • application이 usecase로 외부로 향하는 의존성이 없어 독립적으로 기능이 작동한다.
  • 단일책임 원칙과 공통 폐쇄원칙으로 다른 이유로 변경되는 것들을 분리, 동일 이유로 변경되는 것들을 묶는다.
    • 다른이유 ex) UI부분과 업무 규칙부분이 같은 Usecase에 있다면 분리
    • 업무규칙과 상관없는 database,쿼리언어를 application으로부터 분리

🌟 유즈케이스 결합 분리

유즈케이스 자체도 독립적으로 분리해야한다.
장바구니에 물건을 추가하는 것과 결제하는 것이 다른 속도로, 다른 이유로 변경된다.
각 유즈케이스는 UI의 일부, 어플리케이션의 특화 업무 규칙의 일부,db기능의 일부 등을 사용한다.
유즈케이스가 세세하게 분리되어있어야 구체화 단계인 저수준에서 겹치지 않게 된다.
또한 새로운 유즈케이스 추가에서도 다른 유즈케이스를 고려하지 않고 추가할 수 있게 된다.

도메인 예시

public class CartItem {
    private Long productId;
    private int quantity;
}

유스케이스 예시

//InPort용

public interface CartCheckout{
    Long checkout();
}

public interface CartAddItem{
    void addItem(CartItem item);
}



//OutPort용

public interface CartRemoveItem{
    void removeItem(Long productId);
}

public interface LoadCartItems{
    List<CartItem> getItems();
}


각각의 Usecase interface들을 세부적으로 분리하여 adapter Layer에서
요구되는 usecase별로 독립적으로 가져다 쓸 수 있다.

  • 계층을 분리
  • 유즈케이스 분리
  • 독립적인 개발, 배포, 운영이 가능