[클린 아키텍처] 06. 영속성 어댑터 구현하기

Jimin Lim·2023년 6월 6일
0

Architecture

목록 보기
6/23
post-thumbnail

✅ 영속성 어댑터 구현하기

계층형 아키텍처는 영속성 계층에 의존하게 되어 DB주도 설계가 된다. 따라서 의존성을 역전시킬 필요가 있다.

🔗 의존성 역전

아래는 영속성 계층 대신 애플리케이션 서비스에 영속성 기능을 제공하는 영속성 어댑터이다.

영속성 어댑터는 driven or 아웃고잉 어댑터다. 애플리케이션에 의해 호출당할 뿐, 애플리케이션을 호출하는건 아니기 때문이다.

포트는 사실상 인터페이스로, 애플리케이션 서비스와 영속성 코드 사이의 간접적인 계층이다. (의존성 역전)

🔗 영속성 어댑터의 책임

  1. 입력을 받는다.
  2. 입력을 DB 포맷으로 매핑한다.
  3. 입력을 DB로 보낸다.
  4. DB 출력을 애플리케이션 포맷으로 매핑한다.
  5. 출력을 반환한다.
  • (1) 영속성 어댑터는 포트 인터페이스를 통해 입력을 받는다. 입력 모델은 인터페이스가 지정한 도메인 엔티티나 특정 DB연산 전용 객체가 된다.
  • (2), (3) 그 후, DB 쿼리 혹은 변경에 적합한 포맷으로 입력 모델을 매핑한다. jpa 를 사용한다면 jpa 엔티티 객체로 매핑할 것이다.
  • (4), (5) DB응답을 포트에 정의된 출력 모델로 매핑해 반환한다.

영속성 어댑터의 입/출력 모델은 애플리케이션 코어에 있기에 영속성 어댑터 내부를 변경하는 것이 코어에 영향을 미치지 않는다.

🔗 포트 인터페이스 나누기

위처럼 특정 엔티티가 필요로 하는 모든 DB연산을 하나의 리포지토리 인터페이스에 넣어두는 것이 일반적이다. 그럼 DB연산에 의존하는 각 서비스는 단 하나의 메서드만 사용하더라도 '넓은' 포트 인터페이스에 의존성을 갖게 된다.

인터페이스 분리 원칙을 사용해 클라이언트가 자신이 필요로 하는 메서드만 알도록 최대한 특화된 인터페이스로 분리하도록 해보자.

각 서비스는 실제로 필요한 메서드에만 의존하고, 포트 이름이 포트의 역할을 명확하게 잘 표한하고 있다.

🔗 영속성 어댑터 나누기

위에서 하나의 영속성 어댑터를 두었는데, 아래와 같이 영속성 연산이 필요한 도메인 클래스(애그리거트) 하나당 하나의 영속성 어댑터를 분리할 수 있다.

'애그리거트당 하나의 영속성 어댑터' 접근 방식은 추후 바운디드 컨텐스트의 영속성 요구사항을 분리하기 위한 좋은 토대가 된다.

🔗 스프링 데이터 JPA

	Account mapToDomainEntity(
			AccountJpaEntity account,
			List<ActivityJpaEntity> activities,
			Long withdrawalBalance,
			Long depositBalance) {

		Money baselineBalance = Money.subtract(
				Money.of(depositBalance),
				Money.of(withdrawalBalance));

		return Account.withId(
				new AccountId(account.getId()),
				baselineBalance,
				mapToActivityWindow(activities));

	}

jpa entity와 도메인 엔티티를 구별하고 mapper로 매핑해주는 과정을 거친다. Jpa 엔티티는 기본 생성자를 필요로 해서 이전에 Account에 유효한 엔티티만 만들도록 하는 제약조건이 의미없어진다.

🔗 DB 트랜잭션은?

트랜잭션은 하나의 특정 유스케이스에 대해 일어나는 모든 쓰기 작업에 걸쳐있어야 한다. 따라서 어댑터 호출을 관장하는 서비스에 위임해야한다.

@Transactional
public class SendMoneyService implements SendMoneyUseCase {
...
}

✨ 참고자료

https://rutgo-letsgo.tistory.com/331
https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=283437942

profile
💻 ☕️ 🏝 🍑 🍹 🏊‍♀️

0개의 댓글