아키텍처
클린 아키텍처
(클린 아키텍처를 기반으로 작성하였습니다. 개발자라면 클린 아키텍처 책을 꼭 읽어보시길 추천드립니다)
아키텍처의 주된 목적은 시스템을 쉽게 이해하고 쉽게 개발하며 쉽게 유지보수하고 또 쉽게 배포하게 하는 것이다. 즉 시스템의 생명주기를 지원하는 것과 관련된 비용은 최소화하고 개발자의 생산성을 최대화하는 데 있다.
이를 위해서 고수준의 정책(업무 규칙 또는 비즈니스 로직)과 저수준의 세부사항(입출력 장치, 데이터베이스, 웹 시스템, 프레임워크, 통신 프로토콜 등)을 분리해야 한다. 물론 정책은 어떤 경우에도 세부사항에 의존하지 않아야 하며 세부사항에 대한 결정은 최대한 미룰 수 있어야 한다. 즉, 정책과 세부사항이 의존할 추상 인터페이스를 우선적으로 설계하고 구현해야 한다.
독립성: 결합 분리
개발자의 생산성을 가장 떨어뜨리는 요인은 결합이다. 결국 아키텍트의 목표는 결합을 최적으로 분리하는 것이다. (Divide and conquer - 나누어서 정복하라 - 복잡한 문제를 푸는 가장 기본적인 방법)
UI, 업무 로직, 데이터베이스와 같이 수준에 따라 수평으로 계층을 분리해야 하고 유스케이스는 수직으로 분리해야 한다.
계층과 유스케이스의 결합을 분리하는 방법은 아래와 같이 다양하다.
- 소스 수준 분리 모드 : 하나의 실행 파일로 로드된다. 모노리틱 구조라고 부른다.
- 배포 수준 분리 모드 : 결합이 분리된 컴포넌트가 jar 파일, DLL과 같이 독립적으로 배포할 수 있는 단위로 분할되어 있다.
- 서비스 수준 분리 모드 : 의존하는 수준을 데이터 구조 단위까지 낮출 수 있고(DB까지 분리할 수 있고) 소스와 바이너리 변경에 대해서 완전히 독립적이다.
아래로 내려갈 수록 물리적 분리 수준이나 독립성은 높아지지만 개발 시간 측면이나 시스템 자원 측면에서 비용이 많이 든다.
좋은 아키텍처는 모노리틱 구조로 태어나서 이후 독립적으로 배포 가능한 단위로 성장하고 또 독립적인 마이크로서비스로 성장해 나가는데 무리가 없어야 한다. 즉, SOLID 원칙에 따라 계층과 유스케이스를 잘 나누는 것이 분리하는 방법보다 훨씬 중요하다. (프로젝트 초기에는 어떤 모드가 최선인지 알기 어렵다 따라서 비용이 가장 낮은 모노리틱으로 시작하고 계층이나 유스케이스 분리에 집중하는 것이 좋을 것이다)
경계: 선 긋기
소프트웨어 아키텍처는 선을 긋는 기술이다. 즉 고수준 업무 규칙과 저수준 GUI, DB 등과 경계를 짓는 것이다.
- 업무 규칙과 GUI와의 경계
- 업무 규칙과 DB와의 경계
합쳐서 보면 업무 규칙을 중심으로 나머지는 인터페이스를 통해 (쉽게 교체 가능한) 플러그인 형태로 연결하는 것이다.
클린 아키텍처
의존성 규칙
소스 코드 의존성은 반드시 안쪽으로, 고수준의 정책을 향해야 한다.
엔터티
핵심 업무 규칙을 캡슐화
유스케이스
애플리케이션에 특화된 업무 규칙 (의존성 역전을 위해 인터페이스 어댑터를 위한 추상 인터페이스를 포함한다)
인터페이스 어댑터
인터페이스 구현체 (컨트롤러, 프리젠트, 게이트웨이)
프레임워크와 드라이버
외부 세부사항 (웹, UI, DB 등)
전형적인 시나리오 예시
프로젝트 패러독스
안타깝게도 프로젝트 초반에 잘 모르는 상태에서 많은 결정들을 해야한다. 그러다보니 진행하면서 결정들을 번복하는 상황이 자주 발생할 수 밖에 없다. 가장 고수준인 업무 규칙 조차도 변경된다. 당연히 뛰어난 아키텍트라면 이런 점을 감안하여 DB나 GUI와 같은 세부사항과의 연결은 최대한 미루어야 한다. 즉, 엔터티, 유스케이스와 테스트를 위한 인터페이스 어댑터 목업을 우선적으로 구현하고 충분히 검증한 후에 외부 세부사항을 연결하기를 권장한다.