도메인 모델과 리포지터리를 구현할 때 자주 사용하는 기술 중 하나는 JPA이다. JPA는 자바의 ORM 표준이다.
- 리포지터리 인터페이스는 애그리거트와 같이 도메인 영역에 속하고 리포지터리를 구현한 클래스는 인프라스트럭처 영역에 속한다. 가능하면 리포지터리 구현 클래스를 인프라스트럭처 영역에 위치하여 인프라스트럭처에 대한 의존을 낮춰야 한다.
public class ChangeOrderService {
@Transactional
public void changeShippingInfo(OrderNo no, Shippinginfo newShippinglnfo) {
Optional<Order> orderOpt = orderRepository.findById(no);
Order order = orderOpt.orElseThrow(() -> new OrderNotFoundException());
order.changeShippinglnfo(newShippinglnfo);
)
- 수정기능 - @Transactional 사용으로 인해 스프링 프레임워크의 트랜잭션 범위에서 실행된다. 메서드 실행이 끝나면 트랜잭션이 커밋된다. 이때 JPA는 트랜잭션 범위에서 변경된 객체의 데이터를 DB에 반영하기 위해 UPDATE 쿼리를 실행한다. 위처럼 changeShippingInfo()를 통해 애그리거트가 변경되면 JPA는 변경 데이터를 DB에 반영하기위해 UPDATE 쿼리를 실행한다. 수정 후, 따로 저장하는 save() 메소드를 사용할 필요없다.
- 삭제기능 - 삭제 요구사항이 있더라도 데이터를 실제로 삭제하는 경우는 많지않다 관리자 기능에서 삭제한 데이터까지 조회하는 경우도 있고 데이터 원복을 위해 일정 기간동안 보관해야할 때도 있다. 그래서 soft-delete 방법을 많이 사용한다.
- hard-delete: 데이터를 실제로 삭제한다.
- soft-delete: 데이터에 isDeleted와 같은 삭제 여부에대한 삭제 플래그를 사용해서 나타내는 것, 실제로 삭제되지는 않는다.
스프링 데이터 JPA를 이용한 리포지터리 구현
- 스프링 데이터 JPA는 지정한 규칙에 맞게 리포지터리 인터페이스를 정의하면 리포지터리를 구현한 객체를 알아서 Spring bean으로 등록해준다. 인터페이스를 직접 구현하지 않고 사용이 가능하다.
- org.springframework.data.repository.Repository< T , ID > 인터페이스 상속 (T는 엔티티 타입 ID는 식별자 타입)
@Entity
@Table(name = "purchase_order")
@Access(AccessType.FIELD)
public class Order{ // 엔티티
@EmbeddedId
private OrderNo number; // 식별자
}
import org.springframework.data.repository.Repository;
import java.util.Optional;
public interface OrderRepository extends Repository<Drder, OrderNo> {
Optional<Order> findById(OrderNo id);
void save(Order order);
}
- 위와 같이 Order 엔티티와 OrderRepository를 작성할 수 있다.
매핑 구현
- 주문 애그리거트에서 루트 엔티티인 Order는 JPA의 @Entity로 매핑한다. orderer는 밸류 타입 프로퍼티이므로 @Embedded 로 매핑한다.
import javax.persistence.Entity;
@Entity
@Table(name = "purchase_order")
public class Order {
...
@Embedded
private Orderer orderer;
}
- Order에 속하는 Orderer는 밸류이므로 @Embeddable 로 매핑한다.
@Embeddable
public class Orderer {
// 컬럼 이름 변경하기 위해 @AttributeOverride 애너테이션 사용
@Embedded
@AttributeOverrides(
@AttributeOverride(name = "id", column = @Column(name = "orderer_id"))
)
private Long memberId;
@Column(name = "orderer_name")
private String name;
}
- 엔티티 위에 @Access를 사용하여 JPA 매핑처리를 프로퍼티 방식으로 선택할건지 필드 방식으로 선택할건지 구현할 수 있다. JPA의 구현체인 Hibernate는 @Access를 이용해 @id나 @EmbeddedId가 어디에 위치했느냐에 따라 접근 방식을 결정한다. @Id나 @EmbeddedId가 필드에 위치하면 필드 접근 방식을 선택하고 get 메서드에 위치하면 메서드 접근 방식을 선택한다.
- 필드 접근 방식: @Access(AccessType.FIELD)
- 메서드 접근 방식:@Access(AccessType.PROPERTY)
AttributeConverter를 이용해 밸류 매핑 처리하기
- AttributeConverter는 밸류 타입과 컬럼 데이터 간의 변환을 처기하기 위한 기능을 정의하고 있다.
package javax.persistence;
public interface AttributeConverter<X,Y>{
public Y convertToDatabaseColumn (X attribute);
public X convertToEntityAttribute (Y dbData);
}
- X -> 밸류 타입, Y -> DB타입
- convertToDatabaseColumn( entity value -> DB column value)
- convertToEntityAttribute( DB column value -> entity value)
예를 들어, 엔티티에서는 사용자가 만든 객체인 Money가 있다. 이 Money를 DB에 저장할 땐 Integer로 저장하고 싶다고 가정하고 위에 converter를 사용해보자.
package com.myshop.common.jpa;
import com.myshop.common.model.Money;
import javax.persistence.Attributeconverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class MoneyConverter implements Attrib니teConverter<Money, Integer> {
@Override
public Integer convertToDatabaseColumn(Money money) {
return money = = null ? null : money.getValue();
}
@Override
public Money convertToEntityAttribute(Integer value) {
return value = = null ? null : new Money(value);
}
}
- AttributeConvert 인터페이스를 구현한 클래슨느 @Converter 애너테이션을 적용 해 사용한다. autoApply 속성값을 통해 true 면 모든 Money타입의 프로퍼티에 대해 converter가 자동으로 적용한다. false 면 프로퍼티 값을 사용할때 converter를 직접 지정해야한다.
// autoApply 속성 true인 경우
@Entity
@Table(name = "purchase_order") public class Order {
@Column(name = "total_amounts")
private Money totalAmounts; // MoneyConverter를 적용해서 값 변환
}
// autoApply 속성 false인 경우
@Entity
@Table(name = "purchase_order") public class Order {
@Column(name = "total_amounts")
@Convert(converter = MoneyConverter.class) // 직접 컨버터 지정
private Money totalAmounts; // MoneyConverter를 적용해서 값 변환
}
2편에서 계속 ....
'책 > 도메인 주도 개발' 카테고리의 다른 글
[DDD] 도메인 주도 개발 시작하기 CH_5. 스프링 데이터 JPA를 이용한 조회 기능 (0) | 2023.02.18 |
---|---|
[DDD] 도메인 주도 개발 시작하기 CH_4. 리포지터리와 모델 구현 2편 (0) | 2023.02.13 |
[DDD] 도메인 주도 개발 시작하기 CH_3.Aggregate(애그리거트) (0) | 2023.02.06 |
[DDD] 도메인 주도 개발 시작하기 CH_2.아키텍쳐 개요 (2) | 2023.02.02 |
[DDD] 도메인 주도 개발 시작하기 CH_1. 도메인 모델 (0) | 2023.01.29 |