계층으로 구성하기
코드를 구조화하는 첫 번째 접근법으로 계층을 이용하는 것으로서, 다음과 같이 코드를 구성할 수 있다.
buckpal
|--- domain
| |--- Account
| |--- Activity
| |--- AccountRepository
| |--- AccountService
|--- persistence
| |--- AccountRepositoryImpl
|--- web
|--- AccountController
웹 계층, 도메인 계층, 영속성 계층 각각에 대해 전용 패키지인 web, domain, persistence를 뒀다. 의존성 역전 원칙을 적용해서 의존성이 domain 패키지에 있는 도메인 코드만을 향하도록 했다. domain에 AccountRepository 인터페이스를 추가하고 구현부(AccountRepositoryImpl)는 persistence 패키지에 두었다.
최적의 구조가 아닌 이유
1. 애플리케이션의 기능 조각이나 특성을 구분 짓는 패키지 경계가 없다.
여기에 사용자를 관리하는 기능을 추가해야 한다면 web패키지에 UserController, domain에 UserRepository, persistence에 UserRepositoryImpl을 추가해야 한다. 추가적인 구조가 없다면 서로 연관되어 예상치 못한 부수효과가 일어날 수 있다.
2. 애플리케이션이 어떤 유스케이스들을 제공하는지 파악할 수 없다.
AccountService와 AccountController가 어떤 유스케이스를 구현했는지 파악 할 수 없고 기능을 찾기 위해서는 추측을 하거나 찾아봐야 한다.
3. 패키지구조를 통해서는 우리가 목표로 하는 아키텍처를 파악할 수 없다.
육각형 아키텍처 스타일을 따랐다고 추측 할 수 는 있고, 웹 어댑터와 영속성 어댑터를 찾기 위해 web, persistence 패키지의 클래스들을 조사해볼 수만 있을 뿐이다.
기능으로 구성하기
buckpal
|--- account
| |--- Account
| |--- AccountController
| |--- AccountRepository
| |--- AccountRepositoryImpl
| |--- SendMoneyService
기능으로 묶은 새로운 그룹은 account와 같은 레벨의 새로운 패키지로 들어가고 패키지 외부에서 접근되면 안되는 클래스들에 대해 package-private 접근 수준을 이용해 패키지 간의 경계를 강화 할 수 있다.
하지만, 패키징 방식보다 아키텍처의 가시성을 훨씬 더 떨어뜨린다. 어댑터를 나태는 패키지명이 없고, 인커밍포트, 아웃고잉 포트를 확인할 수 없다. 심지어 도메인 코드와 영속성 코드 간의 의존성을 역전시켜서 SendMoneyService가 AccountRepository인터페이스만 알고 있고 구현체는 알 수 없도록 했음에도 불구하고, package-private 접근 수준을 이용해 도메인 코드가 실수로 영속성 코드에 의존하는 것을 막을 수없다.
아키텍처적으로 표현력 있는 패키지 구조
헥사고날 아키텍처에서 구조적으로 핵심적인 요소는 엔티티, 유스케이스, 인커밍/아웃커밍 포트 인커밍/아웃고잉 어댑터이다.
buckpal
|--- account
|--- adapter
| |--- in
| | |--- web
| | |--- AccountController
| |--- out
| | |--- persistence
| | | |--- AccountPersistenceAdapter
| | | |--- SpringDataAccountRepository
|--- domain
| |--- Account
| |--- Activity
|--- application
|--- SendMoneyService
|--- port
|--- in
| |--- SendMoneyUseCase
|--- out
|--- LoadAccountPort
|--- UpdateAccountStatePort
이 패키지 구조는 '아키택처-코드 갭'과 '모델-코드 갭'을 효과적으로 다룰 수 있는 강력한 요소이다. 이러한 용어는 대부분의 소프트웨어 개발 프로젝트에서 아키텍처가 코드에 직접적으로 매핑될 수 없는 추상적 개념이라는 사실을 보여준다. 만약 패키지 구조가 아키텍처를 반영할 수 없다면 시간이 지남에 따라 코드는 목표하던 아키턱체로부터 멀어지게 된다.
하지만 위 패키지구조는 정확히 아키텍처를 알 수 있다. 만약 클라리언트를 바꾸고 싶다면 adapter/out/<어댑터이름>으로 찾을 수 있기 때문이다. 또, 패키지가 많아 public이 많아야 할지도 모른다는 생각이 들지만 application계층에서는 port 인터페이스를 통해서 호출하기 때문에 접근 수준을 package-private으로 둬도 무관하다.
의존성 주입의 역할
웹 어댑터와 도메인, 도메인과 영속성 어댑터 사이에 의존성 주입을 할 수 있다.
'Theory > Architecture' 카테고리의 다른 글
영속성 어댑터 구현하기 (0) | 2022.07.02 |
---|---|
웹 어댑터 구현하기 (0) | 2022.06.29 |
유스케이스 작성하기 (0) | 2022.06.21 |
의존성 역전하기 (0) | 2022.06.19 |
계층형 아키텍쳐의 문제는 문제일까? (0) | 2022.06.16 |
댓글