본문 바로가기

코딩 스탠다드/OOP

DDD 관점에서 연관관계

*상위 문서: 코드 리뷰: 불완전한 객체에서 나타난 문제들

*데이터 모델링이나 메모리가 아닌 DDD의 모델링 관점에서 연관관계에 관한 글입니다. (맨 아래 추가 1 참조)

A. 개요

    한창 JPA를 사용하면서 엔티티끼리 서로를 가지는 연관관계로 모델링을 하는 방법에 익숙했습니다. 그러나 DDD를 관해 공부하고 토론하며 에그리거트 간의 경계를 지키기 위해서는 서로 다른 에그리거트의 경우 약한 참조로 구성하는 것이 좋을 수도 있겠다 생각이 들어 엔티티의 연관관계에 대해 알아보기 위해 내용을 정리해 보았습니다. 

 

* 애그리거트(Aggregate): 하나의 비즈니스 트랜잭션 일관성의 경계로 단일 트랜잭션에서 같은 비즈니스의 규칙과 제약에 따라 같이 변경되는 범위.

  • '로그인', '회원가입'처럼 논리적인 개념으로 하나의 비즈니스 로직에 속한 엔티티들의 모임입니다. 같은 트랜잭션에 있어야 한다는 점이 중요합니다. 
  • 그래서 하나의 엔티티는 여러개의 애그리거트에 속할 수도 있습니다.

B. 기준 

1. 에그리거트 경계 (맹수호빵님)

  • 직접 참조
    • 같은 애그리거트에 속해 있다면 사용됩니다.
    • 이 경우 엔티티간의 생명주기도 비슷합니다. 
  • 간접 참조
    • 다른 애그리거트에 속해 있다면 같은 트랜잭션에서 변경이 생길 필요가 없습니다.
    • 이 경우 간접 참조를 하는 것이 올바른 애그러거트의 경계를 가지게 합니다. 

2. 엔티티의 라이프 사이클

  • 직접 참조
    • CRUD가 되는 시점이 서로 비슷하여 같은 트랜잭션에서 동시에 생성되거나 변경되는 경우. 
  • 간접참조
    • CRUD가 서로 다른 시점에서 각각의 트랜잭션으로 일어나는 경우.

3. 성능 고려사항 (oth3410님, 맹수호빵님)

  • 직접 참조
    • 애그리거트의 기능에 따라즉시로딩을 할지 지연로딩을 할지 결정해야 한다.
    • 자주 같이 조회되는 경우 직접 참조로 한번에 가져와서 N+1 문제 해결.
    • 또는 JPA사용시 지연로딩을 통해 필요한 부분만 조회하여 가능.  
  • 간접참조
    • 불필요한 데이터의 조회를 줄여 대용량 데이터 다룰 때 메모리 사용 최적화.  
    • 구현의 복잡도 감소.
    • 빈번한 변경이 있는 경우 영향 범위 감소. 

C. DDD에서의 객체 간 참조 

    DDD에서는 약한 참조로 만드는 것이 느슨한 결합을 위해 선호되지 않을까 생각했지만, 기준 1번에서도 말하고 있듯이 꼭 그렇지만은 않습니다.  강한 참조/ 약한 참조는 비즈니스 요구사항에 의해 설계된 도메인 모델의 결과일 뿐입니다. 다만, 엔티티간의 결합이 강한 참조로 되어 있다면 같은 에그리거트에 속해 있고 같은 에그리트거트 루트를 통해 트랜잭션에서 일관성을 관리 받게 되어야 합니다. 그래서 같은 에그리거트에 속하지 않으면서 강한 참조를 가진 경우 애그리거트의 범위를 설정하는데 오류가 있었을 수 있습니다. 이 경우 트랜잭션에서 사용되는 도메인 리포지토리가 여러 개라면 여러 에그리거트 루트가 일관성을 관리하고 있다는 뜻이니, 사실상 두개의 애그리거트가 하나의 트랜잭션에서 관리되고 있는 모습이라 비즈니스 로직을 다시 확인하여 두개의 트랜잭션으로 나누거나 강한 결합을 약한 결합으로 바꾸는 것이 좋습니다. 

 

예시 시나리오. 

 

Help를 등록시 Place라는 장소를 findbyID로 강한 결합 -> 사실상 'Help 등록'과 'Place 조회'라는 각각의 애그리거트가 합쳐진 형태이고 'Place 조회'는 필요하지 않음. -> 강한 결합대신 placeId로 변경

 

D. 사용 방법 

1. 직접 참조의 예시

 

    직접참조가 필요한 경우 트랜잭션을 늘리지 않고 인프라 영역에서 단일 트랜잭션으로 처리해야 합니다. JPA를 이용한다면 연관관계(@OneToOne, @ManyToOne)를 이용하거나 네이티브 쿼리를 이용한다면 join문등을 통해 관련 테이블들을 조회하여 하나의 비즈니스를 담은 트랜잭션(에그리거트)에서 같이 사용되도록 구성하면 됩니다. 

 

//POJO의 경우
public class Book {

    private Long id;
    
    private Author author;
    
    //POJO로 만든다면 Author를 Book 생성시 넣어 주어야 합니다. 
    public Book(Long id, Author author){
    	if(id == null || author == null) return;
    	this.id = id;
        this.author = author
    }
}

//JPA의 경우
@Entity
public class Book {
    @Id
    @GeneratedValue
    private Long id;
    
    @ManyToOne
    private Author author;
}

 

 

2. 간접 참조의 예시

 

    간접 참조는 id값만 가지고 오면되어서 트랜잭션 관리의 어려움이 없습니다. 

 

//POJO의 경우
public class Book {

    private Long id;
    
    private Long authorId;
    
    public Book(Long id, Long authorId){
    	if(id == null || authorId == null) return;
    	this.id = id;
        this.authorId = authorId
    }
}

//JPA의 경우
@Entity
public class Book {
    @Id
    @GeneratedValue
    private Long id;
    
    @Column(nullable = false)
    private Long authorId;
}

 

 

 

추가 1.

    초기에 용어에 생소할때 찾아보며 헷갈렸던 부분이라 추가하자면, 데이터 모델링에서 강한 개체, 약한 개체,  또는 메모리 관점에서 강한참조, 약한 참조가 아닌  DDD의 모델링에서 관점에서 연관관계에서  관련된 글입니다. 

-> 데이터 모델링에서 약한 개체는 DDD에서 값객체(Value Object)로 볼 수 있고, 약한 개체와 강한 개체와의 관계는 "DDD 관점"에서  객체 참조를 통해 조회하는 강한 참조 관계로 볼 수 있습니다.

 

추가 2.

    같은 말이지만 여러 용어로 블로그들에서 불리고 있습니다.

    - 개체 = 엔티티

    - 강한 참조 = 직접 참조 = 강한 관계 = 강한 결합 = 강한 결합도

    - 약한 참조 = 간접 참조 = 약한 관계 = 약한 결합 = 약한 결합도

 

 

출처

simplify-len님의 [특강]도메인 주도 설계의 사실과 오해 2부

devty님의 간접매핑과 DDD

미스터카멜레온님의[도메인 주도 설계의 사실과 오해 5기] 2일차 내용 정리

Picbel님의 직접 참조와 간접 참조의 구분?

감자퓨레님의 JPA 직 참조/ 간접 참조

DunDung님의 DDD에서는 왜 간접 참조를 더 권장할까?

doyoungKim님의 [DDD]간접 참조(feat.JPA)

민준님의 조영호님의 오프라인 DDD 세미나 정리 -2

맹수호빵님의 JPA/DDD 관점에서의 직접 참조/ 간접 참조

oth3410님의 애그리거트간의 참조