강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

rhdrlehf5님의 프로필 이미지
rhdrlehf5

작성한 질문수

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

애그리거트와 헥사고날 아키텍처

애그리거트의 repository

작성

·

20

0

안녕하세요 토비님!
애그리거트를 사용할 때 질문 사항이 있습니다.

예) A도메인 B도메인이 있다 A는 애그리거트 루트이고 B는 A의 부속 엔티티이다.A와 B는 일대다, 다대일의 양방향 의존성을 가진다.B는 A를 통해서만 조작될 수 있다.이 때 B를 생성하거나 업데이트 할 때 B의 repository는 어디에 존재해야 하는가?

@Entity
public class A {
    
    @OneToMany(mappedBy = "media", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<B> bs = new ArrayList<>();
    
    public void updateNumber(long n){
        this.bs.stream().forEach(b -> b.update(n);
    }
}

@Entity
public class B {
    
    @ManyToOne(fetch = FetchType.LAZY)
    private A a;
    
    private long number;

    public void update(long n){
        this.number = n;
    }
}

이렇게 되어 있다고 할 때 변경 가능성을 생각할 때(물론 엔티티에서 이미 jpa에 기술을 사용하고 있긴하지만) B의 repository를 따로 가지는게 맞나요? 만약 따로 가진다면 B의 repository가 A repository에서 의존하여 처리 되어야 하나요?

jpa에 완전 종속적으로 사용하면 B가 따로 repository를 가질 필요 없는데 순수함을 유지하지 하려 하니 이 부분에서 고민이 되네요. 아니면 이런 고민 자체가 잘못된걸까요?

 

 

답변 2

0

토비님의 프로필 이미지
토비
지식공유자

애그리거트는 이상적으로 보이지만 실제 설계하기란 쉽지 않습니다. 고려할 점도 많고, 절대적인 정답이 있는 것도 아닙니다. 항상 여러가지 트레이드 오프가 따르게 됩니다.

만약 이 둘을 에그리거트로 묶고 A를 루트로 결정했다면 리포지토리는 A에 대해서만 있어야 합니다. B는 A가 어떤 식으로든 로딩될 때 그때 같이 올라오도록 조인이 되거나 탐색이 되게 만듭니다. 따라서 B 리포지토리는 필요하지 않습니다. 애그리거트라면 생명주기도 일치해야 하니 A와 B는 같이 등록되고, 수정되고, 삭제될 겁니다. 항상 같은 트랜잭션 안에서 변경이 일어나죠. JPA의 cascading과 각종 페칭 전략만 적용해 놓으면 B 리포지토리가 존재할 이유가 없습니다.

애그리거트 내부의 엔티티는 루트를 통해서만 접근하게 되고, 다른 엔티티와 직접 연결되지 않으며, 따로 조회되지도 않아야 합니다.

이렇게 사용하는 것이 원칙입니다. 하지만 현실적으로 성능이나 기타 필요에 의해서, 특히 어드민이나 데이터 분석쪽의 조회 때문에 B 엔티티를 직접 조회할 필요도 있습니다. 단순 조회만 있고 이후 변경이 일어나지 않는다면 이건 애그리거트 관점에서 조금 예외로 둬도 괜찮습니다. 그런 경우라면 B는 특별한 조회 로직을 가질 뿐이니 굳이 CRUD를 다 지원하는 리포지토리로 만들 필요는 없고, A 리포지토리 안에 B에 대한 조회 메소드를 하나 추가해두면 됩니다.

그런데 애플리케이션이 발전하다보면 A와 B가 다른 트랜잭션으로 쪼개지거나, B가 직접 다른 엔티티와 관계를 맺어야 자연스러운 경우가 발생할 수도 있습니다. 이때는 어쩔 수 없이 A, B를 각각 다른 애그리거트로 쪼개면 됩니다. 이건 도메인 중심의 개발을 할 때 개발이 진행되면서 자연스럽게 일어나기도 하는 일입니다.

A가 루트이고 항상 A를 통해서 B에 접근한다면 B의 @ManyToOne은 제거할 수도 있습니다. 그러면 A의 컬렉션이 오너십을 가지는 구조로 매핑 설정을 하게 됩니다. B의 추가도 당연히 A를 통해서 일어나야겠죠. 아마 파트2에서는 이런 구조의 애그리거트 예가 등장할 겁니다. 그때 좀 더 자세한 설명을 해볼게요.

rhdrlehf5님의 프로필 이미지
rhdrlehf5
질문자

넵 답변 감사합니다!

0

안녕하세요, 인프런 AI 인턴입니다. rhdrlehf5님!

애그리거트와 관련된 질문을 주셨군요. 애그리거트의 루트와 내부 엔티티 간의 리포지토리 설계는 도메인 주도 설계(DDD)에서 중요한 고려사항 중 하나입니다.

애그리거트 루트와 B의 리포지토리

  1. 애그리거트 루트의 책임: DDD에서 애그리거트 루트는 해당 애그리거트 내의 모든 엔티티와 값 객체를 관리할 책임이 있습니다. 따라서, 일반적으로 애그리거트 내에서의 변경은 루트를 통해 이루어져야 합니다. B는 A를 통해서만 조작될 수 있다는 의미는 주어진 도메인 규칙을 따르는 것입니다.

  2. 리포지토리 위치:

    • B의 독립된 리포지토리: BA를 통해서만 조작되어야 하므로, 독립된 B 리포지토리가 존재할 필요가 없습니다. B의 변경은 A의 리포지토리와 해당 엔티티를 통해 관리되고, B를 직접적으로 조작하는 것은 설계 원칙에 맞지 않습니다.
    • A 리포지토리에서의 관리: BA와 강하게 결합되어 있어 A 리포지토리에서 관리하는 것이 맞습니다. 이는 애그리거트의 일관성을 유지하는 데 도움이 됩니다.
  3. JPA와의 관계: JPA를 사용하면서도 도메인 순수성을 유지하는 것은 고민할 만한 부분입니다. JPA의 cascade 옵션을 사용하여 AB의 생명주기를 관리하도록 설정할 수 있습니다. 이는 변경 감지, 저장, 삭제 등의 작업이 자동으로 A 리포지토리를 통해 수행됨을 의미합니다.

요약 및 조언

애그리거트는 트랜잭션의 경계이며, 일관성을 유지하는 단위입니다. 따라서, B와 같은 내부 엔티티는 독립적인 리포지토리를 가져서는 안 되고 A를 통해 간접적으로 관리되는 것이 적절합니다.

추가 참고 링크:
- 애그리거트와 리포지토리 패턴의 이해
- JPA와 애그리거트 설계

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

rhdrlehf5님의 프로필 이미지
rhdrlehf5

작성한 질문수

질문하기