Clean-Architecutre #5-4부 - 클린 아키텍처 컴포넌트에 대해

Clean-Architecutre #5-4부 - 클린 아키텍처 컴포넌트에 대해

클린 아키텍처의 구성 컴포넌트들

26. 메인(Main) 컴포넌트

저자는 메인 컴포넌트가 나머지 컴포넌트를 생성하고 조정하며 관리한다고 했다.
메인 컴포넌트는 가장 세부적이고 낮은 수준의 정책이다.

✒︎ Q: 왜 메인이 가장 저수준이지?.. 설계도상 항상 맨 위에 있는데

그림과 헷갈리면 안된다. 아키텍처의 핵심은 애플리케이션의 ‘비즈니스 로직’ 혹은 ‘도메인 로직’을 극대화하는 것이다.
나머지는 그저 비즈니르 로직을 구현하기 위한 구체화 도구로 생각하면 된다.
핵심 비즈니스 로직은 애플리케이션의 가장 ‘높은’ 수준의 정책을 나타내고,
이렇게 정의되는 ‘높고 낮음’의 척도는 단순히 코드의 실행 순서나 프로그램의 구조를 나타내는 것이 아니라, 어떤 것이 더 ‘중요한가’, ‘변경될 확률이 낮은가’에 대한 척도라고 보면 된다.

메인은 시스템의 진입을 유도한 후에 고수준의 부분으로 제어권을 넘기는 역할을 맡는다.
메인을 아키텍처 경계 바깥의 플러그인들과 동일시하여 생각하면 쉽다.


27. ‘크고 작은 모든’ 서비스들

요즘 유행하는 서비스 지향 아키텍처마이크로서비스 아키텍처는 서비스를 중심으로 분리되어 있다.

서비스지향아키텍처 저자가 말한 단순 서비스분리 아키텍처이다.
기능적 관점에서만 분리해 새 기능이 추가될 경우, 기능 횡단적 관심사 분리가 안되어 수정시 전체수정을 해야한다.

✒︎ Q: SOA의 서비스와 저자의 컴포넌트의 차이

들어가기에 앞서, 27장에서 나오는 저자의 서비스라는 용어의 조작적 정의가 뭘까 고민해봤다.

두 개념은 비슷해 보이지만, 사용 목적과 범위, 그리고 의존성과 연계성 등에서 차이가 있다.
SOA의 서비스는 대개 시스템 간의 상호작용을 목적으로 분리에 초첨이 맞춰져 있다.
클린 아키텍처의 컴포넌트는 보통 하나의 애플리케이션 내부에서의 모듈화와 재사용성을 목적으로 한다.

저자는 단순 기능 중심으로 분류되기만 한 제한된 서비스라는 정의를 내려 컴포넌트와의 차이점을 드러내고, 클린 아키텍처의 경계 구분의 핵심은 기능 위주가 아니라 컴포넌트의 의존성에 있다고 말하는 것 같다.

✒︎ Q: 유행중인 두가지 아키텍처는 클린 아키텍처와 부합될까?

저자는 두 아키텍처의 포커싱을 단순히 서비스로 둔다면, 클린 아키텍처와 부합하지 않는다고 한다.

  • 상호 결합이 철저하게 분리되는 것처럼 보인다.
  • 개발과 배포 독립성을 지원하는 것처럼 보인다.

서비스 중심으로 분리하는 것이, 결합도와 개발/배포 독립성이 높아지는 것을 완벽히 보장되지 않는다.

  • 서비스는 분리된 코드나 기능을 제공하며, 이는 ‘횡단 관심사’를 일으킬 수 있다.
  • 서비스의 분리는 단순히 ‘값비싼 함수 호출’에 불과할 수 있으며, 이 자체로 아키텍처를 결정짓지는 않는다.

🌟서비스 분리가 클린 아키텍처가 될 것이다 가 잘못된 생각인 이유

단순히 서비스를 분리하면 결합도가 낮아지는 장점은 있다.
그러나 이것은 단순히 소규모의 변수 단위에서 이뤄지는 결합 분리이다.
결국에 서비스 간의 결합이 코드 수준에서는 잘 분리되어 있을지라도, 공유되는 자원(예: 데이터베이스) 때문에 실질적으로는 강한 결합을 가질 수 있다.
이러한 강한 결합은 시스템이 변화할 때 모든 서비스에 영향을 줄 가능성이 높아진다.
예를 들어, 공유된 데이터베이스의 테이블 컬럼이나 스키마가 변경되면, 이에 의존하는 모든 서비스가 수정되어야 할 수 있다.

✒︎ 서비스 분리 결합도 문제의 본질

이것이 바로 ‘횡단 관심사(Cross-Cutting Concerns)’에 대한 문제이다.
하나의 변경이 전체 시스템에 미치는 영향이다. 이러한 횡단 관심사를 잘 관리하지 못하면, 서비스는 물리적으로는 분리되어 있지만 논리적으로는 강하게 결합되어 있는 상태가 된다.

서비스 중심 아키텍처에서 여러 서비스가 공유 자원(예: 데이터베이스, 메시지 큐 등)에 대한 의존성을 가질 경우, 이러한 공유 자원이 바로 횡단 관심사가 된다.
예를 들어, 한 서비스에서 데이터베이스의 특정 테이블을 수정하면, 그 변경 사항은 다른 서비스에도 영향을 줄 수 있다.
이렇게 되면 서비스들 사이에 간접적인 결합이 발생하게 된다.

이러한 간접결합은 시스템 전체에 대한 복잡성을 증가시키며, 변경이나 확장을 어렵게 만든다.
따라서 횡단 관심사를 잘 관리해야만 이러한 간접결합을 최소화하고, 각 서비스가 독립적으로 유지보수와 확장이 가능하도록 할 수 있다.

✒︎ 개발과 배포 독립성 문제의 본질

서비스중심으로 분리함에 따라 예측되는 또 다른 이점은 전담팀이 서비스를 소유하고 운영하는 것이다.
각 서비스는 전담팀이 작성하고 유지보수하며, 운영하는 책임을 질 수 있게 된다.
그리고 이게 개발 및 배포 독립성이 확장 가능한 것으로 간주하는 이유이고 독립적인 팀 단위별로 서비스를 맡아 책임질 수 있게 된다.

저자는 이것의 극히 일부만 인정한다.

  1. 대규모 엔터프라이즈 시스템은 서비스 기반 시스템 외에 모노리틱, 컴포넌트 기반 시스템으로도 구축 가능하다.
  2. 서비스라고 해서 항상 독립적으로 개발,배포,운영이 가능한 것은 아니다. 데이터나 행위에서 어느정도 결합되면 결합된 정도에 맞게 조정해야 한다.

횡단관심사 해결에는 계층별 DTO 분리, CQRS, Event주도 개발로 횡단 관심사를 분리하는 방법이 있다.


그렇다면 무엇이 중요한가?

중요한 것은 서비스 자체가 아니라 어떻게 서비스가 시스템 전체와 연결되어 있는가
즉, 서비스 내에 그어진 경계와 경계를 넘나드는 ‘의존성 규칙’을 어떻게 준수하고 있는가이다.
아키텍처는 시스템의 고수준 정책과 저수준의 세부사항을 분리하는 경계에 의해 정의된다.
의존성 규칙을 따르며 아키텍처 경계를 넘나드는 함수 호출이 아키텍처를 정의하는 요소이다.

결국 변경시에 얼마나 힘든가가 척도이다.

🌟컴포넌트 기반 아키텍처에서 횡단적 관심사를 분리하는 방법

다형적으로 확장할 수 있는 클래스 집합을 생성해 새로운 기능을 처리하도록 한다.

컴포넌트기반에서설계 컴포넌트 기반의 아키텍처에서 설계한 모습

주요한건 경계가 생기고, 다형성으로 의존도를 낮추고, 횡단 관심사를 분리한다는 것이다.
추상체와 구현체가 분리되어 필요에 따라 구현체를 다형성을 이용해 교체 가능하다.
TaxiFinder, Selector 등의 종류가 추가되더라도 새로운 컴포넌트를 추가하고 구현하기만 하면 된다.
이후 UI에서 받아 ComponentFactories에서 생성의뢰를 맡기면 된다.

🌟그럼 서비스기반에서도 가능할까?

YES! 가능하다.

서비스는 SOLID 원칙대로 설계 가능하며, 컴포넌트 구조를 갖출 수도 있다.
서비스의 범위는 하나 이상의 jar 파일에 포함되는 추상 클래스들의 집합이다.
jar를 추가하고 구현체들을 만들어 확장을 하면 된다.

서비스컴포넌트설계 이전의 서비스 설계와 달리 추상체로 Service를 만들고, 필요에 의해 구현체를 만들어 사용한다.

🌟 아키텍처의 경계는 서비스에 있지 않다.

지금까지 결국 클린 아키텍처의 중요 포인트인 경계는 결국 서비스 사이에 있지 않다는 것을 길게 설명했다.
아키텍처의 경계를 결정하는 것은 서비스 내에 위치한 컴포넌트들이다.

서비스는 개발하거나 확장할 때 유용하긴 하지만, 아키텍처에서는 큰 역할을 하지 않는다.
아키텍처를 결정하는 건 시스템 내부에서 어떻게 의존성이 연결되는가이다.
서비스는 하나의 ‘경계’ 안에 있을 수도 있고, 여러 ‘경계’로 분리된 다수의 컴포넌트를 뭉쳐 만들 수도 있다.

서비스내부아키텍처설계 의존성 규칙을 준수한 서비스 컴포넌트식 설계

아키텍처의 경계는 서비스가 아니다.

28. 테스트 경계

테스트는 아키텍처의 일부이다. 테스트를 설계의 영역으로 넣지 않고 잘못 짠다면 깨지기 쉽고 유지보수가 어려워진다.
수정 한 번에 테스트 1000개씩 어긋나기 시작하면 수정이 점점 두려울 것이고, 테스트는 더 이상 도움이 되지 않는다.

테스트는 태생적으로 의존성 규칙을 따르는데, 테스트가 아키텍처 원 제일 외부에 있다고 생각하면 의존성은 항상 내부로 향한다.
그래서 테스트를 고려하지 않고 설계하면 아래와 같아진다.

  • 테스트가 깨지기 쉬워지고 시스템은 뻣뻣해져서 변경이 어렵다.
  • 테스트가 시스템에 강하게 결합된 탓인데, 시스템이 변경되면 테스트도 함께 변경되어야 하는 단점이 있다.
  • 시스템에서 생긴 변화에 예민해지면 테스트가 순식간에 어긋난다.
  • 개발자들은 수정에 두려움을 갖기 시작한다.

테스트는 값비싼 자원(DB,UI)은 건너뛰고, 시스템을 테스트 가능한 특정 상태로 강제하여야 한다.


29. 클린 임베디드 아키텍처

하드웨어의 발전 속도는 대단하다.
무어의법칙 굳이 증명하지 않아도 무어의법칙은 다들 들어봤을 것이다.
하드웨어에 결합되게 소프트웨어를 설계하면, 하드웨어가 발전하면서 소프트웨어를 다시 설계해야 한다.
저자는 이런 교체되어야 하는 강결합 소프트웨어도 펌웨어(firmware)라고 부른다.(딱딱해)

켄트 백(Kent Beck)과 저자에 따르면, 아래 세 가지 활동을 통해 소프트웨어를 설계해야 한다고 한다.

  1. 먼저 동작하게 만들어라.
    -> 소프트웨어가 동작하지 않는다면 사업은 망한다.
  2. 그리고 올바르게 만들어라.
    -> 코드를 리팩터링해서 당신을 포함한 나머지 사람들이 이해할 수 있게 만들고, 요구가 변경되거나 요구를 더 잘 이해하게 되었을 때 코드를 개선할 수 있게 만들어라.
  3. 그리고 빠르게 만들어라.
    -> 코드를 리팩터링해서 ‘요구되는’ 성능을 만족시켜라.

그런데 보통은 올바르게 만들어라 (유지보수와 이식도가 높은 클린 아키텍처로 만드는 방향인 것 같다.) 를 지키지 않는다고 한다.


    [소프트웨어]
      [OSAL]
       [OS]
      [HAL]
      [펌웨어]
     [하드웨어]

임베디드 소프트웨어도 하드웨어,OS와의 결합도를 낮추고 추상화를 진행해야 한다고 얘기한다.
하드웨어 자체는 소프트웨어 아키텍처 입장에서는 선택사항이므로 최대한 최후의 결정되는 시점까지 미뤄진다.
그러므로 하드웨어와의 결합도를 낮추는 것이 중요하다.
하드웨어는 어찌됐건 가장 경계 바깥에 최최최말단에 존재한다고 보면 된다.


출처

27장 그림 출처