SEB_BE_43 / 23.02.21 회고

rse·2023년 2월 21일
0

코드스테이츠_BE_43

목록 보기
39/65
post-thumbnail

오늘

  • Spring Data JDBC 도메인 엔티티 & 테이블 설계

애그리거트(Aggregate)

= 비슷한 범주의 연관된 업무들을 하나로 그룹화 해놓은 그룹
한마디로 폴더 같은 느낌.

저번부터 만들고 있던 커피 주문 애플리케이션의 도메인 애그리거트.
여기서 애그리거트는 회원, 주문, 커피 3개가 된다.

애그리거트 루트

애그리거트 안에는 1개 이상의 도메인이 있는데 그 중에서 해당 애그리거트를 대표하는 도메인을 DDD에서는 애그리거트 루트(Aggregate Root) 라고 한다.

애그리거트 루트 선정 기준

각 애그리거트 내에서 다른 도메인과 직간접적으로 연결되어 있는 도메인 엔티티를 생각해보면 된다.


빨간색으로 칠해진 것이 애그리거트 루트가 되겠다.

회원정보 밑에 회원 포인트가 있고,
주문정보 안에 커피 정보도 들어가 있을 것이다.

DDD(Domain Driven Design)란?

도메인 위주의 설계 기법이다.
모든 기능을 도메인 모델 위주로 돌아가는 기법.

  • 도메인?
    도메인은 업무 영역이라고 생각하면 된다. 현실에서 한개의 업무 영역.파트 같은 느낌.

애그리거트 객체 매핑 규칙

  1. 모든 엔티티 객체의 상태는 애그리거트 루트를 통해서만 변경할 수 있다.
    애그리거트 루트 이외의 다른 엔티티의 상태를 벼경하려면 무조건 애그리거트 루트를 통해서만 변경 할 수 있다는 말이다.
    즉 애그리거트 루트가 다른 엔티티에 대한 객체를 직간접적으로 참조 할 수 있다는 의미.

    이유는 도메인 규칙에 대한 일관성을 유지해야하기 때문.

  2. 엔티티 간에는 객체로 참조한다.

  • 단 동일한 애그리거트 내의 엔티티끼리 참조할 경우에만.
  1. 애그리거트 루트 대 애그리거트 루트 간의 엔티티 객체 참조

예로들어 배달이라고 생각했을 때 이미 배달중인 주문의 주소를 고객이 갑자기 변경해버린다면 어떻게 될까. 이미 배달원은 변경해버리기 전 주소로 배달을 할 것이고, 고객은 주소를 변경했으니 변경한 주소로 올 것이라고 생각 할 것이다.
이런 상황을 대비해서 주소 변경은 주문 확인 중 일때만 가능하다던지, 이런 검증 과정이 있어야 할 것이다.
애그리거트 루트 를 통해서만 접근이 가능하게 하면 애그리거트 루트가 검증을 할 것이고, 일관되게 규칙에 대한 검증을 할 것이다.

💡강사님이 알려주신 애그리거트 잘 나누는 방법!

  1. 같은 애그리거트에 포함되는 도메인은 같이 생성되고 같이 사라진다.
    회원의 예를 들자면 회원정보가 사라졌는데 회원 포인트가 남아있을 이유는 없다.
    애그리거트 루트가 사라졌을 때 같이 사라질 도메인을 생각하면 된다.

  2. 도메인의 변경 주체가 다르다면 각각 다른 애그리거트에 속한다.
    음식과 리뷰의 주체는 같은가?
    음식은 식당에서 요리사가 만드는 것이고, 리뷰는 손님이 먹고 평가를 남기는 것이다.
    음식의 관리는 식당에서 하고, 리뷰는 손님이 관리한다.
    즉 주체가 다르다.

Domain Entity


출처 : 코드스테이츠
우리가 만들 도메인 엔티티 클래스의 설계& 테이블의 설계. 이대로 구현을 해보자.

Order 도메인 엔티티는 Member 도메인 엔티티와 N:1관계다.
Member가 1이고 Order가 N. 한 사람은 여러개의 주문이 가능하니까.
그리고 회원 애그리거트 에서 Member가 루트고, 주문 애그리거트에서 Order가 루트이기 때문에

그럴경우 Member의 객체를 참조하는 것이 아니고, 애그리거트 루트 와 루트끼리는 ID로 참조해야한다.
AggregateReference를 사용하면 외래키로 Member 애그리거트의 Id를 참조할 수 있도록 감싸준다.

1대 N의 관계의 애그리거트 루트 간 ID는 AggregateReference 클래스로!
@Table 애너테이션을 붙이면 원하는 테이블 명으로 데이터베이스 테이블 생성 가능.
원래는 도메인 엔티티 클래스 명으로 생성됨.

@Id 애너테이션은 해당 테이블의 기본키로 지정하겠다는 뜻.


Order 애그리거트 루트 와 Coffee 애그리거트 루트 역시 루트 대 루트 참조이기에 ID를 참조하지만 아까와는 다르게 N대N 관계이므로 참조방식이 다르다.

N대N일 경우 1대N, N대1로 관계를 중간에서 풀어 줄 엔티티가 하나 필요하다.
그 엔티티가 CoffeeRef다. (Set<CoffeeRef> 라고 되어 있는 부분.)

@MappedCollection 애너테이션은 엔티티 클래스 간에 연관관계를 맺어주는 정보를 의미한다.
CoffeeRef 엔티티 클래스가 Order_Coffee 테이블과 매핑되는 클래스다.

그럼 Order_Coffee테이블의 컬럼 중 Orders 테이블과 관계를 맺어주는 컬럼은 무엇일까?
Orders 테이블의 order_id(기본키)를 Order_Coffee 테이블에서 외래키로 가지는 것.

그래서 @MappedCollection 애너테이션을 써서 (idColumn 을 사용하면 자식테이블에 해당하는 외래키의 컬럼명을 지정할 수 있다.)

@MappedConllection (idColumn = "Order_ID", keyColumn = "ORDER_COFFEE_ID")
라고 지정한다면 keyColumn은 외래키를 포함하고 있는 테이블의 기본키 컬럼명을 지정하는 것이니
Orders 테이블의 자식 테이블인 Order_Coffee테이블의 기본키는 Ordercoffeeid가 되는 것이다.

그럼 Order 엔티티 클래스와 Coffee 엔티티 클래스를 이어주는 CoffeeRef 엔티티를 보자.

위에서 지정해준 것과 같은 것을 확인 할 수 있다.
orderCoffeeId가 @Id 애너테이션을 붙임으로 기본키로 지정이 되었고, @Table 애너테이션을 붙여줌으로 "ORDER_COFFEE" 라는 테이블 명이 되었다.


sql 파일에 SQL문으로 스크립트를 추가해주자.
ORDER_COFFEE 부분의 스크립트를 보면 FK로 Order_id 와 Coffee_id 두개를 외래키로 갖고 있는 것을 알 수 있다.
PK로는 order_coffee_id를 갖고 있다.

Service & Repository

Repository는 데이터 액세스 계층에서 데이터베이스와 상호작용 역할을 하는 인터페이스를 말한다.
DDD에서 사용.


Optional부분의 findByEmail은 쿼리 메서드(Query Method) 정의를 이용했다.

Spring Data JDBC에서는 find + By + SQL 쿼리문에서 where절의 컬럼명 + (where 절 컬럼의 조건이 되는 데이터) 
형식으로 쿼리 메서드를 정의하면 조건에 맞는 데이터를 테이블에서 조회한다.
또한 where절의 조건 컬럼을 여러개 설정하고 싶다면 And를 사용하면 된다.
위 코드로 예를 들면 findByEmailAndName 이런식으로.
조건은 (String email, String name) 이 되겠다.

주의점

findBy ~ 뒤에 오는 컬럼명은 내부적으로 테이블의 컬럼명으로 변경해주지만 
Spring JDBC는 엔티티 클래스를 바라보고 작업하기 때문에 꼭! 엔티티 클래스의 멤버 변수명을 적어줘야 한다.

아까와는 다르게 @Query라는 애너테이션을 넣어주고 있다.
이 애너테이션을 넣으면 개발자가 직접 쿼리문을 작성해서 질의를 할 수 있도록 해준다.
:coffeeId 는 findByCoffee(Long coffeeId) 의 coffeeId 변수 값이 채워지는 동적 쿼리 파라미터이다.

변수의 값이 null 일 수도 있고 아닐 수도 있을 때 Optional.ofNullable() 을 통해서 null값을 허용할 수 있다.
그래서 name 값이 null이여도 ifPresent가 에러없이 진행된다.

profile
기록을 합시다

0개의 댓글