1.계층 기반 패키지
2.기능 기반 패키지
3.포트와 어댑터
4.컴포넌트 기반 패키지
34장. 빠져 있는 장
클린 아키텍처는 잠시 제쳐놓고, 설계, 코드 조직화와 관련된 접근법을 살펴본다.
I 계층기반패키지
1.가장 단순한 첫번째 설계방식이고 전통적인 수평 계층형 아키텍처이다.
2. 계층기반패키지란 ? 기술적인 관점에서 해당 코드가 하는일을 기반해 그 코드를 분할한다. 각 계층은 “업무규칙”으로 유사한 종류의 것들을 묶는 도구로 사용된다. 엄격한 계층형 아키텍처 경우 계층은 반드시 바로 아래 계층에만 의존해야 한다.

OrdersController : 웹기반 요청처리
OrdersService : 주문관련 “업무규칙" 정의
OrdersServiceImpl : OrderService구현체
OrdersRepository : 저장 정보에 접근하는 방법정의 인터페이스
JdbcOrderRepository : OrdersRepository 구현체
계층형 아키텍처 특징
1) 장점
처음 시작하기에 적합한 아키텍처이다. (다수의 책, 교육, 샘플코드등이 말한다)
엄청난 복잡함을 겪지 않고 무언가를 동작시켜주는 아주 빠른 방법이다.
2) 단점
소프트 웨어가 커지고 복잡해 지면 모든 코드를 담기 부족하다고 깨닫게 되고, 더 잘게 모듈화 할지 고민하게 된다.
업무 도메인에 대해 아무것도 말해주지 않는다.
II 기능 기반 패키지
- 기능 기반 패키지란 ? 기능 기반 패키지는 서로 연관된 기능, 도메인 개념, Aggregate Root(데이터 변경의 단위로 연관객체의 묶음)에 기반하여 수직의 얇은 조각으로 코드를 나누는 방식 모든 타입이 하나의 자바 패키지에 속하고, 패키지 이름은 그안에 담긴 개념을 반영해 짓는다.
34.2 그림에서 세 계층이 하나의 패키지로 속하게 된다.
[34.2] 기능 기반 패키지
기능 기반 패키지 특징
1) 장점 - 이제 코드 상위 수준 구조가 업무 도메인에 대해 알려주게 된다. - “주문 조회하기" 유즈케이스 변경시 변경할 코드를 모두 찾는 작업이 쉬워진다. ( 변경 해야할 코드가 모두 한 패키지에 담겨 있기 때문이다)
위 두가지 수평(계층 기반 패키지), 수직적 계층화(기능기반패키지) 지는 개발 차선책이다.
III 포트와 어댑터
- 포트와 어댑터란?
- 육각형 아키텍처, “경계, 컨트롤러, 엔티티” 방식
- 업무/도메인코드와, 프레임워크나 데이터베이스 같은 기술적 세부구현과 독립적이며 분리된 아키텍처를 만들기 위한 구조
- 내부(도메인)과 외부(인프라)로 구성된다.
- 내부 영역은 도메인 개념을 모두포함하나 외부는 UI, 데이터베이스, 서드파티 통합과 상호작용한다.
- 주요 규칙은 외부가 내부에 의존하며, 내부, 외부가 절대 반대로 안된다.
[34.4] 주문 조회하기 유스케이스
OrdersRepository가 Orders라는 간단한 이름으로 바뀌었다.
도메인 주도 설계에서 ‘내부'에 존재하는 모든것의 이름은 반드시 ‘유비쿼터스 도메인 언어' 관점에서 기술하라고 한다.
즉 도메인에 대해 논할때 우리는 '주문’에 대해 말하는 것이지 ‘주문 레파지터리'에 대해 말하는 것이 아니라는 것이다.
유비쿼터스 언어는 개발자와 도메인 전문가가 공통으로 사용하는 언어
IV 컴포넌트 기반 패키지
SOLID, REP, CCP, CRP 그외 다수 조언에는 동감하나 코드를 조직화 하는 방법에서 다른 선택지를 제시하고자 하는데 이 방법을 ‘컴포넌트 기반 패키지' 라고 부른다.
차이점 정리
특징 | SOLID/REP/CCP/CRP | 컴포넌트 기반 패키지 |
---|---|---|
설계 초점 | 클래스와 모듈의 의존성, 재사용성, 변경 가능성 관리 | 시스템 기능(도메인) 단위로 역할 중심의 모듈화 |
패키지 구성 기준 | 변경 이유/재사용/공통성 기준으로 패키지를 나눔 | 각 컴포넌트가 독립적으로 기능을 수행하도록 구성 |
패키지 구성 기준 | 변경 이유/재사용/공통성 기준으로 패키지를 나눔 | 각 컴포넌트가 독립적으로 기능을 수행하도록 구성 |
의존성 관리 | 의존성을 최소화하거나 역전시키는 구조를 강조 | 컴포넌트 내부의 강한 결합을 허용하지만 컴포넌트 간 의존성을 최소화함 |
모듈화 단위 | 클래스나 패키지 | 독립적인 컴포넌트 (자체적으로 완결된 기능) |
계층형 아키텍처 목적
1) 기능이 같은 코드끼리 서로 분리하는 것이다. (웹 관련 코드는 업무 로직으로부터 분리하고, 업무 로직은 다시 데이터 접근으로부터 분리한다.)
2) UI, 업무, 데이터 접근으로 분리하는데 각 계층은 일반적으로 자바 패키지에 해당한다. (UML 클래스 다이어그램)
3) 코드 접근성 관점에서 서비스 인터페이스 ,레파지터리 인터페이스를 접근하려면 public으로 열려 있어야 한다.
4) 엄격한 계층형 아키텍처에서 의존성 화살표는 항상 아래를 향해야 한다.(비순환 의존성 그래프를 유지하기 위해)
5) 몇몇 유스케이스에서는 Controller가 Service를 우회하고 있다. 이런 인접한 계층을 건너뛰는 일이 허용되는 것을 완화된 계층형 아키텍처라 부른다. (CORS 패턴 지키고자 할때)

1.계층형 아키텍처에서 필요한 것들
1) 컨트롤러는 리포지터리에 직접 접근하면 안된다는 원칙
2) 원칙을 강제하고, 개발자를 신뢰함
3) 정적 분석도구를 통해 아키텍처 위반 사항이 없는지 검사
2.이들 규칙은 컴파일 단계가 끝난후 실행되는 단점이 있다.
4) 아키텍처 원칙을 위반시 빌드가 실패하게 하는 방법
3.“컴포넌트 기반 패키지"를 도입해야 하는 이유는 2번 때문이다.
1) 큰 단위의 단일 컴포넌트와 관련된 모든 책임을 하나의 자바 패키지로 묶는데 주안점을 둔다.

2) “업무로직"과 영속성 관련 코드를 하나로 묶는다. 이 묶음을 “컴포넌트"라고 부른다.
[컴포넌트 정의] 컴포넌트는 배포 단위이다. 컴포넌트는 시스템의 구성 요소로, 배포할수 있는 가장 작은 단위이다. 자바의 경우 jar파일이 컴포넌트 이다.
[저자의 정의] 컴포넌트는 멋지고 깔끔한 인터페이스로 감싸진 연관된 기능들의 묶음이다. 애플리케이션과 같은 실행 환경 내부에 존재한다.
- C4 소프트웨어 아키텍처 모델
1) 위 저장의 정의는 C4 소프트웨어 아키텍처 모델을 따른 것이다.
2)소프트웨어 시스템의 정적 구조를 컨테이너, 컴포넌트, 클래스의 측면에서 게층적으로 생각하는 간단한 방법이다.
3) 시스템은 하나 이상의 컨테이너로 구성된다.
4) 각 컨테이너는 하나 이상의 컴포넌트를 포함한다.
5) 각 컴포넌트는 하나 이상의 클래스로 구현된다.
6) 이때 각 컴포넌트가 개별 jar파일로 분리될지 여부는 변경 영향도에 달려 있다.
- 컴포넌트 기반 패키지 접근법의 이점
1) 무언가를 코딩할때 OrdersComponent만 둘러보면 된다.
2) 컴포넌트 내부에서 관심사 분리는 유지된다.(업무로직은 데이터 영속성과 분리되어 있다)
3) 이는 사용자는 컴포넌트 구현과 관련된 세부사항은 알필요가 없다는 것이다.
4) 마이크로서비스나 SOA 아키텍처를 적용했을때 얻는 이점과 유사하다.
5) 큰차이는 결합 분리 모드에 있으며 모노리틱 애플리케이션에서 컴포넌트를 잘 정의 하면 마이크로 서비스 아키텍처로 가는 발판으로 삼을 수 있다.
구현 세부사항엔 항상 문제가 있다
- 자바의 public 지시자를 무분별하게 사용한다는건 프로그래밍 언어가 제공하는 캡슐화 관련 이점을 활용하지 않겠따는 뜻이다.
- 이로 인해 누군가가 구체적인 구현 클래스의 인스턴스를 직접 생성한 코드를 작성하는 일을 절대 막을 수 없으니, 결국 지향하는 아키텍처 스타일을 위반하게 될것이다.
조직화 vs 캡슐화
- 모든 타입을 public 지시자로 지정한다면, 패키지는 단순 폴더와 같이 묶는 방식으로만 전락하게 되어 캡슐화를 위한 메커니즘이 사라지게 된다.
- public 타입을 코드 베이스 어디에서도 사용할 수 있따면 패키지를 사용하는데 따른 이점이 거의 없다. 따라서 사실상 패키지를 사용하지 않는것과 같아지게되고 최종적으로 어떤 아키텍처 스타일로 만들려고 하는지는 아무런 의미가 없어진다. public 지시자를 과용하면 이 장의 앞에서 제시한 네 가지 아키텍처 접근법은 본질적으로 완전히 같아진다.
위 이미지에서 채택하려는 아키텍처 접근법과 아무런 관계 없이 화살표들이 모두 동일한 방향을 가리킨다.
개념적으로 이 접근법들은 매우 다르지만, 구문적으로는 완전히 똑같다.
이처럼 모든 타입을 public 으로 선언시 우리가 실제로 갖게되는것은 수평적 계층형 아키텍처를 표현하는 네 가지 방식에 지나지 않는다.
자바의 접근 지시자는 완벽하진 않지만 무시하면 사서 고생하게 된다. 적절하게 사용시 타입을 패키지로 배치하는 방식에 따라서 각 타입에 접근할 수 있는 정도가 실제로 크게 달라질 수 있다.
만약 다이어그램에서 패키지 구조를 다시 살려서 더 제한적인 접근 지시자를 사용할 수 있는 타입을 (흐리게) 표시하면, 다이그램은 상당히 인상적으로 변한다. 진한게 표시된 요소만 외부에서 접근 가능하도록 열어둠으로써 캡슐화와 은닉화의 장점을 더 잘 살릴수 있게 되는것이다.
다른 결합 분리 모드
소스코드 의존성 분리는 프로그래밍언어가 제공하는 방법 외에도 자바 OSGi나 자바9에서제공하는 모듈 시스템으로 분리할 수 있음.
다른 선택지로는 소스코드 수준에서 의존성을 분리하는 방법임.
포트와 어댑터로 예로들면
업무와 도메인용 소스 코드: OrderSerivce, OrdersServiceImpl, Orders
웹용 소스 코드: OrdersController
데이터 영속성용 소스 코드: JdbcOrderRepository
2, 3번 두 소스 트리는 컴파일 시점에서 업무와 도메인용 소스코드에 의존성을 가지며 업무와 도메인용 소스코드는 웹, 데이터영속성 코드에 대해 알지 못함.
이렇게 분리하려면 → 1, 2, 3 각각은 Maven, Gradle 같은 도구로 서로 분리되도록 구성해야함.
이상적인 해결책이지만 이렇게 나누다 보면 성능, 복잡성, 유지보수 문제에 부딪힘.
더 간단한 방법은
도메인 코드('내부')
인프라 코드('외부')
로 나누는 것임.

결론: 빠져 있는 조언
<고민해볼 사항>
설계를 어떻게해야 원하는 코드 구조로 매핑가능한가?
해당 코드를 어떻게 조직화 할 것인가?
런타임, 컴파일타임에 어떤 결합 분리모드를 적용할것인가?
<수행해야 할 사항>
가능하면 선택사항은 열어두되 실용주의적으로 행해야함.
팀규모, 기술 수준, 해결책의 복잡성을 일정과 예산이라는 제약과 동시에 고려해야함.
선택한 아키텍처 스타일을 강제하는 데 컴파일러의 도움을 받을 수 있는지 고민해야함.
데이터 모델과 같은 영역에 결합되지 않도록 주의해야함.
'IT > architecture' 카테고리의 다른 글
[Clean Architecture 정리] 29장. 클린 임베디드 아키텍처 (0) | 2025.02.15 |
---|---|
[Clean Architecture 정리] 28장. 테스트 경계 (0) | 2025.02.15 |
[Clean Architecture 정리] 27장. '크고 작은 모든' 서비스들 (0) | 2025.02.15 |
[Clean Architecture 정리] 26장. 메인(Main) 컴포넌트 (0) | 2025.02.15 |
[Clean Architecture 정리] 23장. 프레젠터와 험블 객체 (0) | 2025.02.15 |