"2024년, 한 해 동안 작성한 코드 중 변경하고 싶은 코드는 무엇일까?"
위 질문을 던졌을 때, 작년 초에 진행한 사이드 프로젝트의 패키지 구조가 가장 먼저 떠오른다.
도메인 주도개발 (DDD, Domain Driven Development)에 관심을 가진 상태에서 사이드 프로젝트를 진행했기 때문에 도메인 중심으로 패키지 구조를 설계 했다. 사이드 프로젝트는 단순한 기능을 제공하는 소규모 서비스였고 도메인 중심의 패키지 구조는 이런 소규모 서비스에 어울리지 않는다란 느낌을 주었다.
내부적으로 레이어드 아키텍처를 사용했기 때문에 도메인을 기준으로 패키지를 구성한 뒤 내부에 레이어를 위한 별도의 패키지를 구성하는 작업은 비효율적이었기 때문이다.
더 나은 패키지 구조를 적용하기 위해 '도메인 중심의 패키지 구조'와 '레이어드 중심의 패키지 구조'를 살펴보도록 한다.
도메인 중심 패키지
도메인 중심으로 구성된 패키지는 도메인 별로 패키지를 나누고 내부에 레이어를 구성한다.
도메인 중심의 패키지 구조는 다음과 같은 특징을 갖는다.
1. 비즈니스 도메인 기반 조직화
도메인 중심 패키지 구조의 가장 큰 특징은 기술 계층이 아닌 비즈니스 도메인을 기준으로 코드를 조직화한다는 점이다.
전통적인 계층형 구조
com.company.controllers
com.company.services
com.company.repositories
com.company.entities
도메인 중심 구조
com.company.order
com.company.payment
com.company.user
com.company.notification
이러한 접근 방식은 프로젝트 구조에 비즈니스 용어와 개념을 직접적으로 반영하게 함으로써, 개발자와 도메인 전문가 간의 소통을 원활하게 한다. 또한 새로운 팀원이 프로젝트에 합류했을 때 비즈니스 로직을 더 빠르게 이해할 수 있게 도와준다.
2. 높은 응집도와 낮은 결합도
도메인 중심 패키지 구조에서는 관련된 기능이 모두 동일한 패키지 내에 함께 위치한다.
- 주문 처리와 관련된 컨트롤러, 서비스, 엔티티, 레포지토리가 모두 order 패키지 안에 존재
- 결제 처리와 관련된 모든 클래스는 payment 패키지에 집중
따라서, 이 접근 방식은 높은 응집도(High Cohesion)를 갖게 하며, 도메인 간에는 명확한 인터페이스를 통해서만 통신함으로써 낮은 결합도(Low Coupling)를 유지할 수 있다.
3. 변화에 대한 격리
비즈니스 요구사항은 지속적으로 변화한다. 이 때, 도메인 중심 구조에서는 특정 도메인의 변경이 다른 도메인에 미치는 영향을 최소화할 수 있다.
- 주문 프로세스가 변경되어도 사용자 관리 코드는 영향받지 않음
- 결제 시스템을 완전히 재설계하더라도 다른 도메인은 인터페이스만 유지된다면 안전함
이러한 변화 격리(Change Isolation)는 대규모 시스템에서 특히 중요하며, 마이크로서비스 아키텍처로의 자연스러운 전환 가능성을 제공한다.
4. 스케일링 용이성
도메인 중심 패키지 구조는 큰 시스템을 독립적인 도메인 모듈로 자연스럽게 분할할 수 있게 해준다.
- 도메인별로 독립적인 개발과 배포 가능
- 팀 구조를 도메인에 맞춰 조직화할 수 있음 (콘웨이의 법칙 활용)
- 필요에 따라 특정 도메인을 별도 서비스로 분리하는 마이크로서비스 전환 용이
이는 조직이 성장함에 따라 여러 팀이 동시에 작업할 수 있는 명확한 경계를 제공한다.
5. 패키지 내 수직 슬라이싱
도메인 중심 구조에서는 기능 구현에 필요한 모든 계층이 하나의 패키지 내에 존재한다.
- UI부터 데이터베이스까지 전체 스택이 하나의 도메인 패키지 안에
- 새로운 기능 추가 시 한 패키지 내에서 모든 작업 완료
- 코드 탐색과 이해가 용이해짐
이러한 수직 슬라이싱(Vertical Slicing)은 기능 중심 개발(Feature-Driven Development)을 자연스럽게 지원한다.
6. 명확한 의존성 방향
도메인 중심 구조에서는 의존성 방향이 명확하다.
- 내부 도메인 계층이 외부 계층에 의존하지 않음
- 의존성 역전 원칙(Dependency Inversion Principle)을 통한 도메인 모델 보호
- 인프라스트럭처 코드가 도메인 코드에 의존하도록 설계
이는 클린 아키텍처(Clean Architecture)와 헥사고날 아키텍처(Hexagonal Architecture)의 원칙과도 일치한다.
레이어 중심 패키지
소프트웨어 아키텍처는 시스템의 품질과 유지보수성에 직접적인 영향을 미치는 중요한 요소인데, 레이어드 아키텍처(Layered Architecture) 는 다양한 아키텍처 패턴 중에서 가장 널리 사용되는 것 중 하나이다.
레이어드 아키텍처는 애플리케이션을 수평적인 계층으로 분리하는 구조로, 각 계층은 특정 책임을 담당한다. 전통적인 3계층 아키텍처는 프레젠테이션 계층, 비즈니스 로직 계층, 데이터 접근 계층으로 구성되지만, 애플리케이션의 복잡도에 따라 더 많은 계층으로 세분화될 수 있으며 레이어 중심 패키지는 이러한 계층을 기준으로 패키지를 구성한다.
com.wtf2.erp
├── controller (프레젠테이션 계층)
├── service (비즈니스 로직 계층)
├── repository (데이터 접근 계층)
└── entity (데이터 모델) (or domain)
레이어 중심의 패키지 구조는 다음과 같은 특징을 갖는다.
1. 기술적 기반 관심사 분리
레이어드 아키텍처의 가장 큰 특징은 애플리케이션을 기술적 관심사에 따라 수평적 계층으로 분할한다는 점이다.
- 프레젠테이션 계층: 사용자 인터페이스 및 HTTP 요청 처리 (Controller)
- 비즈니스 로직 계층: 핵심 비즈니스 규칙 및 워크플로우 (Service)
- 데이터 접근 계층: 데이터베이스 상호작용 (Repository)
- 데이터 모델 계층: 비즈니스 엔티티 및 값 객체 (Entity)
각 계층은 고유한 책임을 갖고, 특정 기술 스택과 밀접하게 연관되어 있다. 예를 들어, 프레젠테이션 계층은 Spring MVC나 REST API 관련 기술을, 데이터 접근 계층은 JPA나 MyBatis와 같은 기술을 사용한다.
2. 계층 간 명확한 의존성 방향
레이어드 아키텍처에서는 의존성이 상위 계층에서 하위 계층으로 흐르는 단방향 구조를 가진다.
Controller → Service → Repository → Entity
이러한 단방향 의존성은 시스템의 복잡도를 관리하는 데 도움이 된다. 즉, 하위 계층은 상위 계층에 대해 알지 못하므로, 하위 계층의 변경이 상위 계층에 영향을 미치지 않는다.
예를 들어:
- 컨트롤러는 서비스를 알지만, 서비스는 컨트롤러를 모름
- 서비스는 레포지토리를 알지만, 레포지토리는 서비스를 모름
3. 중앙집중식 구조
레이어드 중심 패키지 구조에서는 동일한 유형의 컴포넌트가 하나의 패키지에 집중된다.
com.wtf2.controller
├── UserController.java
├── ProductController.java
└── OrderController.java
com.wtf2.service
├── UserService.java
├── ProductService.java
└── OrderService.java
com.wtf2.repository
├── UserRepository.java
├── ProductRepository.java
└── OrderRepositoryjava
이러한 구조는 특정 기술 영역의 클래스를 쉽게 찾을 수 있게 해주며, 기술적 관심사를 명확하게 분리한다.
4. 관심사의 수평적 분리
레이어드 아키텍처는 관심사를 수평적으로 분리함으로써 단일 책임 원칙(SRP)을 구현한다.
- 프레젠테이션 계층: 데이터 표현과 사용자 상호작용에 집중
- 비즈니스 로직 계층: 핵심 비즈니스 규칙과 워크플로우에 집중
- 데이터 접근 계층: 영속성과 데이터 조작에 집중
이러한 분리는 각 계층이 자신의 책임에만 집중할 수 있도록 하여 코드의 가독성과 유지보수성을 향상시킨다.
어떤 패키지 구조를 사용할까?
도메인 중심의 패키지 구조와 레이어 중심의 패키지 구조의 특징을 살펴보면 둘 모두 장단점이 존재하며, 다음을 고려할 수 있다.
- 도메인 중심 패키지 구조가 적합한 경우
- 비즈니스 도메인이 복잡하고 다양한 경우
- 장기적인 유지보수와 확장이 예상되는 대규모 프로젝트
- 여러 팀이 독립적으로 작업하는 환경
- 시스템을 마이크로서비스로 전환할 가능성이 있는 경우
- 도메인 전문가와의 긴밀한 협업이 필요한 경우
- 레이어드 중심 패키지 구조가 적합한 경우
- 비즈니스 도메인이 단순하거나 명확한 경우
- 중소 규모의 프로젝트
- 팀원들이 전통적인 계층형 구조에 익숙한 경우
- 특정 기술 계층(예: DB 접근 방식)을 변경할 가능성이 높은 경우
- 표준화된 구조가 필요한 경우
- 하이브리드 접근법
- 많은 성공적인 프로젝트에서는 두 접근 방식을 혼합해서 사용
- 최상위 수준에서는 도메인별로 패키지를 나누고, 각 도메인 내부에서는 레이어드 구조를 적용
- 이는 도메인 중심의 명확성과 레이어드 아키텍처의 기술적 관심사 분리라는 이점을 모두 취할 수 있음
즉, 엔터프라이즈 애플리케이션의 경우, 도메인 중심 구조를 기반으로 하되 각 도메인 내부에 레이어드 아키텍처를 적용하는 하이브리드 접근법을 사용할 수있다. 이는 비즈니스 도메인의 명확성을 유지하면서도, 기술적 관심사를 적절히 분리할 수 있기 때문이다. 하지만 소규모 서비스로 시작할 경우 레이어드 중심 패키지 구조를 사용하여 표준화된 구조를 적용한 후 서비스가 확장된다면 단계적으로 도메인 중심의 패키지 구조 리팩토링을 고려할 수 있다 생각한다.
'회고' 카테고리의 다른 글
[항해 플러스 백엔드] 마지막 회고 (4) | 2024.12.11 |
---|---|
[항해 플러스 백엔드] 9주차 회고 (WIL) (0) | 2024.11.25 |
[항해 플러스 백엔드] 8주차 회고 (WIL) (0) | 2024.11.16 |
[항해 플러스 백엔드] 7주차 회고 (WIL) (1) | 2024.11.09 |
[항해 플러스 백엔드] 6주차 회고 (WIL) (2) | 2024.11.02 |