계층으로 구성하기
buckapl
|--- domain
| |----- Account
| |----- Activity
| |----- AccountRepository
| |----- AccountService
|--- persistence
| |----- AccountRepositoryImpl
|--- web
| |----- AccountController
JavaScript
복사
문제점
1.
계층으로 코드를 구성하면 기능적인 측면들이 섞이기 쉽다.
2.
애플리케이션의 기능 조각(functional slice)이나 특성(feature)을 구분 짓는 패키지 경계가 없다
3.
애플리케이션이 어떤 유스케이스들을 제공하는지 파악할 수 없다
4.
패키지 구조를 통해서는 우리가 목표로 하는 아키텍처를 파악할 수 없다
기능으로 구성하기
buckapl
|--- account
| |----- Account
| |----- Activity
| |----- AccountRepository
| |----- SendMoneyService
| |----- AccountRepositoryImpl
| |----- AccountController
JavaScript
복사
장점
1.
패키지 경계를 package-private 접근 수준과 결합하면 각 기능 사이의 불필요한 의존성을 방지할 수 있다.
문제점
1.
기능을 기준으로 코드를 구성하면 기반 아키텍처가 명확하게 보이지 않는다
아키텍처적으로 표현력 있는 패키지 구조
buckapl
|--- account
| |----- adapter
| | |----- in
| | | |---- web
| | | | |---- AccountController
| | |----- out
| | | |---- persistence
| | | | |---- AccountPersistenceAdapter
| | | | |---- SpringDataAccountRepository
| |---- domain
| | |----- Account
| | |----- Activity
| |---- application
| | |----- SendMoneyService
| | |----- port
| | | |---- in
| | | | |---- SendMoneyUseCase
| | | |---- out
| | | | |---- LoadAccountPort
| | | | |---- UpdateAccountStatePort
JavaScript
복사
장점
1.
각 아키텍처 요소들에 정해진 위치가 있어 직관적이다.
2.
필요한 경우 하나의 어댑터를 다른 구현으로 쉽게 교체할 수 있다.
3.
DDD 개념을 직접적으로 대응할 수 있다.
문제점
1.
패키지가 많다는 것은 모든 것을 public으로 만들어 패키지 간의 접근을 허용해야 하는거 아닌가?
a.
application 패키지 내에 있는 포트 인터페이스를 통하지 않고서 바깥으로 호출되지 않기 때문에 package-private 접근 수준으로 둬도 된다.
2.
JPA의 1차 캐시 등을 적극 활용할 수 없다.
의존성 주입의 역할
•
어댑터는 그저 애플리케이션 계층에 위치한 서비스를 호출할 뿐이다.
•
영속성 어댑터와 같이 outgoing 어댑터에 대해서는 제어 흐름의 반대 방향으로 의존성을 돌리기 위해 DIP를 이용해야 한다.
•
Controller 가 UseCase 인터페이스를 필요로 하기 떄문에, 의존성 주입을 통해 Service 클래스의 인스턴스를 주입한다.
•
컨트롤러는 인터페이스만 알면 되기 때문에 자신이 Service 인스턴스를 실제로 가지고 있는지도 모른다.
Conclusion
•
우리가 목표로 하는 아키텍처에 가깝게 코드 구조를 만들어 보았으며,
•
다음 장에서는 애플리케이션 계층, 웹 어댑터, 영속성 어댑터를 이용해서 유스케이스를 구현하며 패키지 구조와 의존성 주입에 대해 조금 더 살펴본다.