계층형 아키텍쳐의 문제?
계층형 아키텍쳐란
웹 계층에서는 요청을 받아서 도메인 혹은 비즈니스 계층에 있는 서비스로 요청을 보낸다.
서비스에서 필요한 비즈니스 로직을 수행하고, 도메인 엔티티의 현재 상태롤 조회하거나 변경하기 위해 영속성 계층의 컴포넌트를 호출한다.
약점
DB 주도 설계를 유도 (웹 계층은 도메인 계층에 의존하고, 도메인 계층은 영속성 계층에 의존하기 때문에 자연스럽게 DB 의존하게 된다.)
하지만 우리가 만드는 앱은 보통 상태가 아니라 행동 중심으로 모델링한다.
비즈니스 관점에서는 도메인 로직이 DB 구조보다 우선이 된다.
영속성 계층의 단점
- 모든 것에 접근이 가능하기 때문에 시간이 지나면서 점점 비대해진다.
- 테스트가 어려워진다 (하나의 필드를 조작하더라도 돔메인 로직을 웹 계층에 구현하게 된다. -> 유즈케이스 확장시 많은 도메인 로직을 웹 계층에 추가해 앱 이 전반저긍로 책임이 섞이고 핵심 도메인 로직이 퍼져나갈 확률이 높다.
- 웹 계층 테스트에서 영속성 계층을 (mocking) 해야 한다. 즉 단위 테스트 복작도가 올라간다. 그만큼 단위 테스트 작성에 시간이 더 걸린다.
- 위 이유 때문에 유즈케이스 찾기도 어려워진다.
- 동시 작업이 어려워진다. 계층형은 아키텍쳐는 각 단계를 만들어야 다음 단계로 넘어갈 수 있어서 특정 기능은 동시에 한 명의 개발자만 작업할 수 있다. 그래도 동시 작업시 병합 충돌 (merge conflict) 발생 문제 가능성이 있다.
클린 아키텍쳐
의존성 역전하기
단일 책임 원칙
- 컴포넌트를 변경하는 이유는 오직 하나뿐이어야 한다. (Single Reason to Change Principle)
의존성 역전 원칙
- 코드상의 어떤 의존성이든 그 방향을 바꿀 수 (역전시킬) 수 있다. 도메인 계층에 인터페이스를 도입해 의존성을 역전하고 역속성 계층이 도메인 계층에 의존하게 된다.
육각형 아키텍쳐 특징
- 도메인 엔티티와 상호작용하는 유스케이스가 있고.
- 모든 의존성은 코어(엔티티)를 향한다
- 육각형 바깥에는 앱과 상호작용하는 다양한 어댑터가 있다. (웹 브라우저와 상호작용, 외부 시스템과 상호작용, DB와 상호작용하는 어댑터)
Adapter(controller)의 역할
- HTTP 요청-자바 객체 맵핑
- 권한 검사
- 입력 유효성 검증
- 입력 port(interface) 입력 모델로 맵핑
- 유스케이스 호출, 출력을 HTTP로 맵핑
- HTTP 응답 반환
Persistent adapter의 역할
- 입력 받는다
- 입력을 DB 포맷으로 맵핑한다
- 입력을 DB로 보낸다
- DB 출력을 앱 포맷으로 맵핑한다.
- 출력 반환한다.
Port의 역할
- 각 서비스는 실제로 필요한 메서드에만 의존하게 만든다.
- 좁은 포트로 plug and play 경험을 만든다.
- CRUD 메소드명으로 인터페이스 구현 vs 하나의 인터페이스 안에 각각 CRUD 메소드를 구현
코드 구성하기
in - from web(client)
out - to database(server)
계층으로 코드를 구성하면 기능적인 측면들이 섞이기 쉽다.
BuckPal 코드 구성 예제
payment
ㄴ web
ㄴ AccountController
ㄴ domain
ㄴ Account
ㄴ Activity
ㄴ AccountRepository
ㄴ AccountService
ㄴ persistence
ㄴ AccountRepositoryIml
기능을 기준으로 코드를 구성하면 기반 아키텍쳐가 명확하지 않다
payment
ㄴ account
ㄴ Account
ㄴ Activity
ㄴ AccountRepository
ㄴ AccountRepositoryIml
ㄴ SendMoneyService
ㄴ AccountController
ㄴ user
ㄴ User
ㄴ UserRepository
ㄴ UserRepositoryIml
ㄴ UserRegistrationService
ㄴ UserController
아키텍쳐적으로 표현력 있는 패키지 구조
payment
ㄴ account
ㄴ adapter
ㄴ in
ㄴ web
ㄴ AccountController
ㄴ out
ㄴ persistence
ㄴ AccountPersistenceAdapter
ㄴ SpringDataAccountRepository
ㄴ domain
ㄴ Account
ㄴ Activity
ㄴ application (서비스 계층)
ㄴ SendMoneyService
ㄴ port
ㄴ in
ㄴ SendMoneyUseCase
ㄴ out
ㄴ LoadAccountPort
ㄴ UpdateAccountStatePort
ㄴ user
ㄴ adapter
ㄴ domain
ㄴ application
계층형 - 클린 아키텍쳐 융합 응용 구조
payment
PaymentApplication.java
PaymentConfiguration.java
PaymentConfigurationProperties.java
ㄴ account
ㄴ adapter
web (controller/facade)
ㄴ AccountController
ㄴ AccountControllerTest
persistence (DAO, Implement Port, JPA Entity)
ㄴ AccountPersistenceAdapter (SpringDataAccountRepositoryImpl)
ㄴ SpringDataAccountRepository
ㄴ domain (entity)
model (DTO)
ㄴ AccountDTO
ㄴ ActivityDTO
ㄴ Account
ㄴ Activity
ㄴ application (service)
exception (error handling)
ㄴ LocalErrorHandling.java
utility (domain specific utils)
ㄴ Util.java
service (service logic)
ㄴ SendMoneyServicePortInImpl
ㄴ MoneyTransferProperties (props required in service)
ㄴ ExceptionHandler.java
port (interface)
ㄴ SendMoneyServicePortIn(SendMoneyUseCase)
ㄴ CreateAccountPortOut
ㄴ ReadAccountPortOut (RepositoryInterface) (LoadAccountPortInterface)
ㄴ UpdateAccountStatePortOut
ㄴ DeleteAccountPortOut
ㄴ user
ㄴ adapter
ㄴ domain
ㄴ application
ㄴ common
ㄴ PersistenceAdapter
ㄴ SelfValidating
ㄴ UseCase
ㄴ WebAdapter
참고
만들면서 배우는 클릭 아키텍쳐 - 톰 홈버그
https://github.com/wikibook/clean-architecture
https://reflectoring.io/spring-hexagonal/
https://edykim.com/ko/post/ports-and-adapters-architecture-in-php/
728x90
'800===Dev Docs and License > 이론 문서' 카테고리의 다른 글
Coding Best Practice (0) | 2024.06.01 |
---|---|
Network Subnet Introduced (0) | 2024.05.29 |
소프트웨어 크랙 (해적본)은 어떻게 만들고 개발자들은 왜 못막는 걸까? (0) | 2024.05.25 |
The Compilation Process (0) | 2024.05.25 |
CORS 이해 및 설명 (0) | 2024.05.25 |