리포지터리 기본 기능
스프링 데이터 JPA
엔티티와 밸류 기본 매핑 구현
기본 생성자
필드 접근 방식 사용
AttributeConverter를 이용한 밸류 매핑 처리
public class Length {
private int value;
private String unit;
}
//DB에 100mm로 저장
WIDTH VARCHAR(20)
@Converter(autoApply = true)
public class MoneyConverter implements AttributeConverter<Money, Integer> {
@Override
public Integer convertToDatabaseColumn(Money money) {
return money == null ? null : money.getValue();
}
@Override
public Integer convertToEntittyAttribute(Integer value) {
return value == null ? null : new Money(value);
}
밸류 컬렉션
@ElementCollection
@OneToMany
밸류를 이용한 ID 매핑
@Embeddable
public class OrderNo implements Serializable {
@Column(name="order_number")
private String number;
public boolean is2ndGeneration() {
return number.startsWith("N")'
}
}
// 1세대 시스템의 주문번호와 2세대 시스템의 주문번호를 구분할 떄 주문번호의 첫 글자를 이용할 경우
// 시스템 세대를 구분할 수 있는 기능을 구현할 수 있다.
별도 테이블에 저장하는 밸류 매핑
밸류 컬렉션을 @Entity로 매핑하기
ID 참조와 조인 테이블을 이용한 단방향 M-N 매핑
@Entity
@Table(name = "product")
public class Product {
@EmbeddedId
private ProductId id;
@ElementCollection
@CollectionTable(name = "product_category",
joinColumns = @JoinColumn(name = "product_id"))
private Set<CategoryId> categoryIds;
식별자 생성 규칙이 있다면 엔티티를 생성할 때 식벼라를 엔티티가 별도 서비스로 식별자 생성 기능을 분리해야 한다.
- 식별자 생성 규칙은 도메인 규칙이므로 도메인 영역에 식별자 생성 기능을 위치시켜야 한다.
public class ProductIdService {
public ProductId nextId() {
... // 식별자 생성
}
}
public class CreateProductService {
@Autowired private ProductIdService idService;
@Autowired private ProductRepository productRepository;
@Transactional
public ProductId createProdcut(ProductCreationCommand cmd) {
ProductId id = idService.nextId();
Product product = new Product(id, cmd.getDetail(), cmd.getPrice(), ...);
productRepository.save(product);
return id;
}
public class OrderIdService {
public OrderId createId(UserId userId) {
if (userId == null) throw new IllegalArgumentException("invalid userid: " + userId);
return new OrderId(userId.toString + "-" + timestamp());
}
...
}
public interface ProductRepository {
..
// 식별자를 생성하는 메서드
ProductId nextId();
}
// 리포지터리 구현 클래스에서 알맞게 구현하면 됨
@Entity
@Table(name = "article")
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Long getId() {
return id;
}
}
// 자동 증가 칼럼은 DB의 insert 쿼리를 실행해야 식별자가 생성되므로 도메인 객체를 리포지터리에 저장할 때 식별자가 생성된다.
-> 도메인 객체를 저장한 뒤에 식별자를 구할 수 있다.
→ 이 같은 구조는 구현 기술을 변경하더라도 도메인이 받는 영향을 최소화할 수 있다.
DIP를 적용하는 주된 이유는 저수준 구현이 변경되더라도 고수준이 영향을 받지 않도록 하기 위함이다.
하지만 리포지터리와 도메인 모델의 구현 기술은 거의 바뀌지 않음 → DIP를 완벽하게 지키면 좋겠지만 개발 편의성과 실용성을 가져가면서 구조적인 유연함은 어느정도 유지됨
→ 타협을 적절히 히는것도 합리적인 선택임