์ด ๊ธ์ ์ด๋ก ๊ณผ ์ค์ต, ๋ ํํธ๋ก ๋๋์ด์ ธ ์์ต๋๋ค.
- Repository Pattern ์ ๋ํด์, ์ด๋ก ํธ
- Repository Pattern - ์ค์ ํธ (Spring ์์ DIP ๋ฅผ ํตํด Repository ์ ์ ์ธ๊ณผ ๊ตฌํ ๋ถ๋ฆฌ์ํค๊ธฐ) <- ํ์ฌ ๊ธ
ํด๋น ๊ธ์์ ๋์ค๋ ์ฝ๋๋ github repository-ddd ์์ ํ์ธํ ์ ์์ต๋๋ค.
๋ชฉ์ฐจ
- ์๋ก
- ๋ฌธ์ ์ 1. ๋ณต์ก์ฑ
- ๋ฌธ์ ์ 2. ํ์ฅ์ฑ
- ํด๊ฒฐํด์ผ ํ ๋ฌธ์
- ๊ฒฐ๋ก
์๋ก
์ง๋ ์๊ฐ ์ฐ๋ฆฌ๋ DDD ์์ ์ด์ผ๊ธฐํ๋ Repository Pattern ์ ๋ํด์ ์์๋ณด์๋ค.
์ง๋ ์๊ฐ์ ์ด์ผ๊ธฐํ๋ ๋ด์ฉ์ ๊ฐ๋จํ ์์ฝํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
๋ฐ์ดํฐ๋ฅผ persist ํ๊ณ load ํ๋ ๊ฒ์ Repository ๋ผ๋ ์ธํฐํ์ด์ค๋ก ์ถ์ํํ์ฌ domain layer ์์ ์ค์ ๊ตฌํ ๊ธฐ์ ์ ๋ํด์ ๋ชจ๋ฅด๊ฒ ํ๋ค
๊ทธ๋ ๋ค๋ ์๋ฆฌ๋ Domain ๊ณผ Infrastructure ๋ ์๋ก ์ฝ๋๋ฒ ์ด์ค ์์์ ๊ฒฉ๋ฆฌ์์ผ ๋์จํ ๊ฒฐํฉ์ ์ ์งํด์ผ ํ๋ค๋ ๊ฒ์ด๋ค.
์ด๋ฒ ์๊ฐ์๋ ์ง๋ ์๊ฐ์ ๊ฐ๋ ์ ์ผ๋ก๋ง ์ค๋ช ํ๋ ๋ฌธ์ ์ ๋ค์ ์ค์ ๋ก ๋ง๋ฅ๋ค์ด๋ฉด์ Domain ๊ณผ Infrastructure ๊ฐ ํผ์ฌ๋ ์ฝ๋๋ ์ด๋ค ๋ฌธ์ ๊ฐ ์๋์ง ์์๋ณผ ๊ฒ์ด๋ค.
๊ทธ๋ฆฌ๊ณ Domain ๊ณผ Infrastructure ๋ ์๋ก ์ฝ๋๋ฒ ์ด์ค ์์์ ๊ฒฉ๋ฆฌ์์ผ ๋์จํ ๊ฒฐํฉ์ ์ ์งํจ์ผ๋ก์จ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๊ฒ์ด๋ค.
์ด๋ ๊ฒ ํจ์ผ๋ก์จ ๋ฐ์ํ ์ ์๋ ์ฌ๋ฌ ๋ฌธ์ ๋ค๋ ํ์ธํด๋ณด๊ณ ๋ด๊ฐ ๋ด๋ฆฐ ๊ฒฐ๋ก ์ ์ด์ผ๊ธฐํด๋ณด๋๋ก ํ๊ฒ ๋ค.
๋จผ์ ๋ฌธ์ ์ ์ ๋ํด์ ์ด์ผ๊ธฐํด๋ณด์. ์๋์ ์ฝ๋๋ Order ๋ผ๋ ์ฃผ๋ฌธ ๊ฐ์ฒด ํ๋์ ๋ํ ์ ์์ด๋ค.
@Entity(name = "orders")
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order {
public static Order create(Long userId) {
Long id = LongIdGenerator.gen();
return new Order(id, userId, new ArrayList<>(), 0L);
}
public static Order by(Long id, Long userId, List<Long> orderItems, Long totalPrice) {
return new Order(id, userId, orderItems, totalPrice);
}
@Id
private Long id;
private Long userId;
@ElementCollection
private List<Long> orderItems;
private Long totalPrice;
private Order(Long id, Long userId, List<Long> orderItems, Long totalPrice) {
this.id = id;
this.userId = userId;
this.orderItems = orderItems;
this.totalPrice = totalPrice;
}
public void add(Product product) {
orderItems.add(product.getId());
totalPrice += product.getPrice();
}
// more ...
}
์ ์ฝ๋์๋ ๋๊ฐ์ง ๋ฌธ์ ์ ์ด ํฌ๊ฒ ๋๋ฌ๋๋ค.
- ๋ณต์ก์ฑ
- ํ์ฅ์ฑ
ํ๋์ฉ ํ์ธํด๋ณด๋๋ก ํ๊ฒ ๋ค.
๋ฌธ์ ์ 1. ๋ณต์ก์ฑ (Domain ๊ณผ Infrastructure ๊ฐ ํผ์ฌ๋ ์ฝ๋)
์ด๊ฒ ๋ฌด์จ ๋ง์ผ๊น?
์ ์ฝ๋๋ฅผ ๋ณด๋ฉด Domain ๊ณผ Infrastructure ๊ฐ ํผ์ฌ๋์ด์์ด ๋ณต์ก์ฑ์ด ๋๊ปด์ง๋ ์ฝ๋์ด๋ค.
๋ณต์ก์ฑ์ ๋ค๋ฅธ ํํ์ผ๋ก ํด๋ณด์๋ฉด, Order ๋ผ๋ ๋๋ฉ์ธ์ด ๊ฐ์ ธ์ผ ํ ๋น์ฆ๋์ค ๋ก์ง๊ณผ, Order ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๊ธฐ ์ํด์ ํ์ํ ์ฝ๋๋ค์ด ํจ๊ป ๋ค์์ฌ ์๋ค๋ ๊ฒ์ด๋ค.
- ๋๋ฉ์ธ ๋ก์ง :
add(Product product)
๋ฉ์๋ ๋ฑ๋ฑ - ์ ์ฅํ๊ธฐ ์ํ ๋ก์ง :
@Entity
,@Id
,@ElementCollection
๊ณผ ์ฌ๋ฌ Builder,AllArgsConstructor
๋ฑ๋ฑ
๋น์ฆ๋์ค ๋ก์ง์ด add(Product product)
๋ง ์กด์ฌํจ์๋ ๋ถ๊ตฌํ๊ณ ๋น์ฆ๋์ค์ ๋ฌด๊ดํ ์ด๋
ธํ
์ด์
์ด ๋์ง๋์ง ๋ถ์ด์๊ณ ๋๋ฉ์ธ ๊ด์ ์์๋ ์ ํ ์ค์ํ์ง ์์ ๋ด์ฉ๋ค์ด ์์ฌ์๋ค.
๊ณผ์ฐ ๋๋ฉ์ธ ๊ด์ ์์ @ElementCollection
์ด๋ผ๋ ์ด๋
ธํ
์ด์
์ด ์ค์ํ ๊น? ์ฃผ๋ฌธ์ ๊ด์ ์์ ํด๋น ๊ฐ์ฒด๊ฐ ์ด๋ค Id ์์ฑ ์ ๋ต์ ๊ฐ๋์ง๊ฐ ์ค์ํ ๊น?
์ ํ ์ค์ํ์ง ์๋ค. ์คํ๋ ค ๋๋ฉ์ธ์ ๋ฌด๊ดํ ๋ด์ฉ์ด ์๊ธฐ ๋๋ฌธ์ ๋์ฑ ๋๋ฉ์ธ์ ์ง์คํ ์ ์๊ฒ ๋๋ค.
์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์์๊น?
๋ต์ ์์ธ๋ก ๊ฐ๋จํ๋ค. ๋ถ๋ฆฌ์ํค์
์๋ก ๋ค๋ฅธ ์ฑ ์์ ๊ฐ๋ ๋๊ฐ์ง ํด๋์ค๋ก ๋ถ๋ฆฌํ์! ํ๋๋ ๋๋ฉ์ธ, ๋น์ฆ๋์ค ๋ก์ง์ ๊ด์ฌ์ ๊ฐ๋ ๊ฐ์ฒด, ๋ค๋ฅธ ํ๋๋ ์ ์ฅ์ ๊ด์ฌ๊ฐ๋ ๊ฐ์ฒด๋ก ๋ถ๋ฆฌํ์
Order.java
@Getter
public class Order {
public static Order create(Long userId) {
Long id = LongIdGenerator.gen();
return new Order(id, userId, new ArrayList<>(), 0L);
}
private final Long id;
private final Long userId;
private List<Long> orderItems;
private Long totalPrice;
private Order(Long id, Long userId, List<Long> orderItems, Long totalPrice) {
this.id = id;
this.userId = userId;
this.orderItems = orderItems;
this.totalPrice = totalPrice;
}
public void add(Product product) {
orderItems.add(product.getId());
totalPrice += product.getPrice();
}
}
์ฌ์ ํ ๋ณต์กํด ๋ณด์ด์ง๋ง ๊ด์ฐฎ๋ค. ํจ์ฌ ๋น์ฆ๋์ค์ ์ด๊ณ ๋๋ฉ์ธ ๋ค์์ก๋ค.
SpringDataJpaOrderEntity.java
@Entity(name = "orders")
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SpringDataJpaOrderEntity {
@Id
private Long id;
private Long userId;
@ElementCollection
private List<Long> orderItems;
private Long totalPrice;
}
์ด์ ์์ํ๊ฒ persist ์ ๊ด๋ จ๋ ์ฝ๋๋ง ๋จ๊ฒ ๋์๋ค.
์ฐ๋ฆฌ๋ Hibernate ๋ฅผ ์ฌ์ฉํ๋ ์ธ๋์ด๊ธฐ ๋๋ฌธ์ Entity ๋ผ๋ ์ด๋ ธํ ์ด์ ๋ง์ผ๋ก๋ ์ฝ๊ฒ ์ ์ฅ ๋์ ๊ฐ์ฒด๋ผ๋ ๊ฒ์ ์๋ฆด ์ ์๋ค.
ํ์ง๋ง ๊ทธ๋ ์ง ์๋ ์ํฉ์ด๋ผ๋ฉด? ๊ฑฑ์ ์๋ค. ๋๋ฌ์์ง๋ ๊ฒ์ SpringDataJpaOrderEntity
๋ฟ์ด๋๊น.
๋ค๋ฅธ ์ด์ผ๊ธฐ์ด์ง๋ง ์ด๋ฆ๋ ์ค์ํ๋ค! ์ SpringDataJpaOrderEntity ๋ผ๊ณ ํ์๊น? domain ์ infrastructure ๊ตฌํ์ฒด๊ฐ SpringDataJpa ์ด๊ธฐ ๋๋ฌธ์ด๋ค. Jpa ๋ฅผ ์ฐ๋๊น JpaOrderEntity ๋ผ๊ณ ํ๋ค? ์ด๊ฒ๋ ์ ์ ํ์ง ์๋ค๊ณ ์๊ฐํ๋ค. jpa ์ spring data jpa ๋ ์๋ก ๋ค๋ฅธ ๊ตฌํ ๊ธฐ์ ์ด๊ธฐ ๋๋ฌธ์ ๋ช ํํ ๋ช ์ํด์ผ ํ๋ค.
๋ฌธ์ ์ 2. ํ์ฅ์ฑ (์๋ก์ด ๋น๊ธฐ๋ฅ์ ์๊ตฌ์ฌํญ ์ถ๊ฐ)
์, ์ด์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ค์๋ ๋น์ฆ๋์ค๊ฐ ์์ฅ์์ ๊ฐ์น๋ฅผ ์ธ์ ๋ฐ๊ณ ์ฌ์ฉ์๋ค์ด ๊ธ๊ฒฉํ๊ฒ ๋ง์ด ๋์ด๋ฌ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
๊ธฐ์กด์ ์ฌ์ฉํ๋ ๊ธฐ์ ์ ์ผ๋ฐ์ ์ธ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์๋๋ฐ, ๋ง์ฝ ์์ฒญ๋๊ฒ ๋น ๋ฅธ ์๋๋ก ๊ฒ์์ด ๊ฐ๋ฅํด์ผ ํ๋ค๊ณ ํด๋ณด์.
๊ทธ๋ฆฌ๊ณ DB Latency ๊ฐ ๋๋ฌด ์ฌํด์ ์ฟผ๋ฆฌ ํ๋์ผ๋ก๋ ๋์ ํ ํด๊ฒฐํ ์ ์๋ ์ํฉ์ด๋ผ๊ณ ๊ตณ์ด ๊ตณ์ด ๊ฐ์ ํด๋ณด์.
๊ฒฐ๊ตญ, ํ์ ํฉ์ ํ์ MySQL ์ด ์๋๋ผ Elasticsearch ๋ฅผ ์ฌ์ฉํด์ผ๋ง ํ๋ค๊ณ ๊ฒฐ์ ๋์๋ค.
๊ทธ๋ผ ์ฒ์ ๋ดค๋ Domain ๊ณผ Infrastructure ๊ฐ ํผ์ฌ๋ ์ฝ๋๋ผ๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
๋๋ฉ์ธ์ ์ญ์ ๋ณต์กํ๊ณ ์ ์ฅํ๋ Repository ๋ ์๋ง ๋ค์๊ณผ ๊ฐ์ ๊ฒ์ด๋ค
public interface OrderRepository extends JpaRepository<Order, Long> {
Order save(Order entity);
Optional<Order> findByUserId(Long id);
}
์ด ์ํฉ์์ ์ ํํ ์ ์๋ ์ ํ์ง๋ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์์ง๋ง, Spring Data ๋ชจ๋์ ์ฌ์ฉํ๋ ๋๋ ์๋ง๋ org.springframework.boot:spring-boot-starter-data-elasticsearch
์์ ์ ๊ณตํ๋ ElasticsearchRepository
๋ฅผ ์ฌ์ฉํ ๊ฒ ๊ฐ๋ค.
public interface OrderRepository extends ElasticsearchRepository<Order, Long> {
Order save(Order entity);
Optional<Order> findByUserId(Long id);
}
๊ด์ฐฎ์๊น? ๊ด์ฐฎ์๊ฒ์ด๋ค.
ํ์ง๋ง ์ด๋ ๋ค๋ฉด ์ด๋จ๊น?
JpaRepository
์์ DeleteAllInBatch()
๋ผ๋ ์๊ทธ๋์ฒ๊ฐ ์กด์ฌํ๋ค.
ํด๋น ๋ฉ์๋๊ฐ ๋ฌด์์ ํ๋์ง๋ ๋ชจ๋ฅด์ง๋ง ์ด๋๊ฐ์์ ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ํฐ์ผ์ด๋ค. ์๋? ElasticsearchRepository
์๋ ํด๋น ์๊ทธ๋์ฒ๊ฐ ์๊ธฐ ๋๋ฌธ์ด๋ค
์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์์๊น? ์ฌ๋ฌ ๋ฐฉ๋ฒ์ด ์๊ฒ ์ง๋ง, ์ด๋ฒ ์ฃผ์ ์ ์ปจํ ์คํธ๋ก ์ด์ด๊ฐ์๋ฉด,
์ญ์ ๋ต์ ์์ธ๋ก ๊ฐ๋จํ๋ค. DIP ๋ฅผ ํ์ฉํ๋ฉด ๋๋ค
์ง๋ ์๊ฐ์ ์๊ฐํ๋ ๊ทธ๋ฆผ์ ๋ค์ ๊ฐ์ ธ์์ ๋ณด์
๊ณผ ๊ฐ์ ํํ๋ก ๊ตฌ์ฑํ๋ฉด ๋๋ฉ์ธ๊ณผ ์ธํ๋ผ๋ฅผ ์ ์ ํ๊ฒ ๋ผ์ด๋ผ ์ ์๋ค.
์์์ ๋๋ฉ์ธ๊ณผ ์์์ฑ ๊ฐ์ฒด๋ฅผ ๋ถ๋ฆฌํ๋ฏ, Repository ์ญ์ ๋ถ๋ฆฌ์ํค์
public interface OrderRepository {
Order save(Order order);
Optional<Order> findByUserId(long userId);
}
๋๋ฉ์ธ์ ์์ ๊ฐ์ด ๋๋ฉ์ธ์ ์กด์ฌํ๋ Repository ๋ Order ๊ฐ์ฒด๋ฅผ ์๋๋ก ํ๊ณ , ์์์ฑ ์ธํ๋ผ ๋ ํฌ์งํ ๋ฆฌ๋ฅผ ์์์ฑ ์ํฐํฐ์ ํจ๊ป ์ฌ์ฉํ๋ฉด ๋๋ค.
public interface SpringDataJpaOrderRepository extends JpaRepository<SpringDataJpaOrderEntity, Long> {
SpringDataJpaOrderEntity save(SpringDataJpaOrderEntity entity);
Optional<SpringDataJpaOrderEntity> findByUserId(Long id);
}
๊ทธ๋ผ ์๋ง ๋ค์๊ณผ ๊ฐ์ ํํ๊ฐ ๋ ๊ฒ์ด๋ค.
ํ์ง๋ง ํ๊ฐ์ง ๋ฌธ์ ๊ฐ ์๊ธด๋ค.
๋๋ฉ์ธ์ ์กด์ฌํ๋ Repository ์ infrastructure ์ ์กด์ฌํ๋ Repository ์ ํํ๊ฐ ์๋ก ๋ฌ๋ผ์ ธ๋ฒ๋ฆฐ๋ค.
๊ทธ๋์ ์ด ์ฌ์ด์ Adapter ๋ฅผ ํ๋ ๋๊ณ , ํด๋น Adapter ๊ฐ Domain ์ Repository ์ Infrastructure ์ Repository ์ฌ์ด์ ๊ท๊ฒฉ์ ๋ง์ถฐ์ฃผ๋ฉด ๋๋ค.
์ฝ๋๋ก ๋ณด์๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒ์ด๋ค.
@Component
@RequiredArgsConstructor
public class SpringDataJpaOrderRepositoryAdapter implements OrderRepository {
private final SpringDataJpaOrderRepository repository;
@Override
public Order save(Order order) {
SpringDataJpaOrderEntity entity = repository.save(convert(order));
return convert(entity);
}
@Override
public Optional<Order> findByUserId(long userId) {
Optional<SpringDataJpaOrderEntity> optional = repository.findByUserId(userId);
if (optional.isEmpty()) {
return Optional.empty();
}
return Optional.of(convert(optional.get()));
}
private SpringDataJpaOrderEntity convert(Order domain) {
return SpringDataJpaOrderEntity.builder()
.id(domain.getId())
.userId(domain.getUserId())
.orderItems(domain.getOrderItems())
.totalPrice(domain.getTotalPrice())
.build();
}
private Order convert(SpringDataJpaOrderEntity entity) {
return Order.by(entity.getId(),
entity.getUserId(),
entity.getOrderItems(),
entity.getTotalPrice());
}
}
ํด๋น Adapter ๋ฅผ Bean ์ผ๋ก ๋ฑ๋กํ๊ณ ๋๋ฉ์ธ์ ์๋ Repository ๋ฅผ ์ฌ์ฉํ ๋, Spring Context ์๊ฒ ๋น์ ์ฃผ์ ๋ฐ์์ ์ฌ์ฉํ๋ค.
๊ทธ๋ฌ๋ฉด ์ฌ์ฉํ๋ ํด๋ผ์ด์ธํธ๋ Domain ์ Repository ์ ์ฌ์ฉํ๋ ๊ฒ์ด์ง๋ง ์ค์ ๋ก ์ ์ฅ์ Infrastructure ์ Repository ๊ฐ ํธ์ถ๋์ด ์ ์ฅ๋ ๊ฒ์ด๋ค.
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository repository;
public Order order() {
return repository.save(new Order());
}
public Order find() {
return repository.findByUserId(1L);
}
}
์ ์ฒด์ ์ผ๋ก ๋๋ฉ์ธ๊ณผ ์ธํ๋ผ๋ฅผ ๋ถ๋ฆฌํ ํจํค์ง์ ๋ชจ์ต์ ํ์ธํด๋ณด์
๋๋ฉ์ธ, ์น, ์ธํ๋ผ์คํธ๋ญ์ฒ๋ฅผ gradle ๋ชจ๋๋ก ๋ถ๋ฆฌ์์ผ ์์กด์ ์ ์ฝ์ ๊ฑธ์ด๋์๋ค.
๋ค์ํ๋ฒ ์ด์ผ๊ธฐํ์ง๋ง ์์ ๋ด์ฉ์ผ๋ก๋ ๋ธ๋ก๊ทธ ๊ธ์ ํ์ ๋ ํน์ฑ์ผ๋ก ์ธํด ์ดํด๊ฐ ์ด๋ ค์ธ ์ ์์ต๋๋ค. ์์ธํ ์ฌํญ์ github repository-ddd ์์ ํ์ธํ ์ ์์ต๋๋ค.
์ด๋ค๊ฐ ์ดํด๊ฐ ์กฐ๊ธ ๋๋๊ฐ?
๋ถ๋ฆฌ๋ฅผ ํจ์ผ๋ก์จ ์ฐ๋ฆฌ๋ ๋ณต์ก์ฑ์ ๋ฎ์ถ๊ณ ํ์ฅ์ฑ์ ๋์ด๋ ์ด์ ์ ์ทจํ ์ ์์๋ค.
ํ์ง๋ง ๋ถ๋ฆฌํ๋ ๊ฒ์ด ๋ง๋ฅ ์ข์ ๊ฒ๋ง์ ์๋๋ค.
๋ช๊ฐ์ง ํด๊ฒฐํด์ผ ํ ๋ฌธ์ ์ ๋ค์ด ์๋๋ฐ, ํ์ธํด๋ณด์
ํด๊ฒฐํด์ผ ํ ๋ฌธ์
ํด๊ฒฐํด์ผ ํ ๋ฌธ์ ๋ค์ด ๊ฝค๋ ์๋ค.
- ๋๋ฌด ๋ง์ ์ปจ๋ฒํ ์ฝ๋
- ํด๋จผ ์๋ฌ
- JPA ์ฌ์ฉ์ Lazy Loading ๋ถ๊ฐ
๋๋ฌด ๋ง์ ์ปจ๋ฒํ ์ฝ๋
์ฐ์ ์ adapter ์ฝ๋๋ฅผ ๋ณด๋ฉด ์ ์ ์๋ฏ์ด, ๋๋ฌด๋๋ ๋ง์ ์ปจ๋ฒํ ์ด ํ์ํ๋ค.
๋ง์ฝ ํ๋์ ์ ๊ทธ๋ฆฌ๊ฑฐํธ์ ๋งค์ฐ ๋ง์ ์ค์ฒฉ ๊ฐ์ฒด๊ฐ ์กด์ฌํ๋ค๋ฉด ์ด๋ป๊ฒ๋ ๊น?
์ปจ๋ฒํ ์ ํ๋๋ผ ์์ฒญ๋ ์๊ฐ์ ์๊ฒ ๋ ๊ฒ์ด๋ค.
๋ด ๊ฒฝํ์ ์ด๋ ์ฝ๋๊ฐ ํ๋์จ์ด๋ก ๊ฐ๋ ์ง๋ฆ๊ธธ์ธ ๋ณ๊ฒฝ์ ๋๋ ค์ ์ด๋ผ๋ ๋งค์ฐ ์ข์ง ์์ ์๊ทธ๋์ ์ฃผ๋๋ผ.
ํด๋จผ ์๋ฌ
์์ ์ปจ๋ฒํ ์ฝ๋์ ์ง๊ฒฐ๋ ๋ด์ฉ์ธ๋ฐ, ์๋์ ์ฝ๋์์ ๋ฌธ์ ์ ์ ์ฐพ์๋ณด์.
@Getter
public class Order {
public static Order by(Long id, Long userId, List<Long> orderItems, Long totalPrice) {
return new Order(id, userId, orderItems, totalPrice, "");
}
private final Long id;
private final Long userId;
private List<Long> orderItems;
private Long totalPrice;
private final String address;
public Order(Long id, Long userId, List<Long> orderItems, Long totalPrice, String address) {
this.id = id;
this.userId = userId;
this.orderItems = orderItems;
this.totalPrice = totalPrice;
this.address = address;
}
// ์๋ต
}
์๋ ๋๋ฉ์ธ Order ์ด๊ณ ์๋๋ ์ธํ๋ผ์คํธ๋ญ์ฒ์ Order ์ด๋ค.
@Entity(name = "orders")
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SpringDataJpaOrderEntity {
@Id
private Long id;
private Long userId;
@ElementCollection
private List<Long> orderItems;
private Long totalPrice;
}
๋ฌธ์ ์ ์ ์ฐพ์ ์ ์๋๊ฐ?
๋ฌธ์ ๋ Order ๊ฐ์ฒด์ address ๋ผ๋ ํ๋๊ฐ ์ถ๊ฐ๋์์ง๋ง ๋๊ตฐ๊ฐ์ ์ค์๋ก ์ธํด์ Entity ์๋ address ๊ฐ ์๋ค.
์ด๋ ์ด์ฉ๋ฉด ๋น์ฐํ๊ฒ ์ง๋ง ๋ช ์์ ์ผ๋ก ํน์ ์ฝ๋์์์ Domain ๊ณผ Infra ์ ์ฐ๊ฒฐ์ด ๋ถ๋ฆฌ๋์๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ ๋ฌธ์ ์ด๋ค.
์ด๋ฅผ ์ปดํ์ผ ๋จ์์ ํ์ธํ ์ ์์ผ๋ ๊ทธ๋งํผ ์์ ์ฑ์ ๋จ์ด์ง ์ ์๋ค.
JPA ์ฌ์ฉ์ Lazy Loading ๋ถ๊ฐ
์ญ์ Converting ์ ์ฐ์ฅ์ ์ด๋ค.
์ ์๋ค์ถ์ด Jpa ๋ Lazy Loading ์ด๋ผ๋ ๊ธฐ์ ์ด ์กด์ฌํ๊ณ ๊ฐ๋จํ ์ด์ผ๊ธฐํ์๋ฉด ์ค์ ์ฌ์ฉ์ด ์์ ๋๋ง ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๋ ์ผ์ข ์ ์ฑ๋ฅ ์ ๋ต์ด๋ค.
ํ์ง๋ง ์ปจ๋ฒํ ์ ํ๋ ๊ณผ์ ์์ ์ค์ ์ฐธ์กฐ๊ฐ ์ด๋ค์ง๊ธฐ ๋๋ฌธ์ Lazy Loading ์์ฒด๊ฐ ์ฌ๋ผ์ง๊ฒ ๋๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ์ด์ ํนํ๋ Proxy ๋ฅผ ์ง์ ๋ง๋ค์ด์ ์ฌ์ฉํด์ผ ํ๋ค๊ณ ๋ ํ๋๋ผ.
ํ์ง๋ง Aggregate ์ ๋ํด์ Lazy Loading ์ด ํ์ํ์ง ์๋ค๊ณ ๋ณด๋ ์๊ฒฌ๋ ์กด์ฌํ๋ค.
ํ์ฌ ๋ฒ์ญ ์์ ์ค์ธ cqrs-journey ํ๊ธ ๋ฒ์ญ์ ๋น์ทํ ์ด์ผ๊ธฐ๊ฐ ๋์จ๋ค.
A ๊ฐ๋ฐ์์ B ๊ฐ๋ฐ์๊ฐ ์ด์ผ๊ธฐ๋ฅผ ๋๋๋ ์ค ๋ค์๊ณผ ๊ฐ์ ๋ง์ ํ๋ค.
I agree. I have found that lazy-loading in the command side means I have it mod- eled wrong. If I don’t need the value in the command side, then it shouldn’t be there.
์ฆ lazy loading ์ด๋ผ๋ ๊ฒ์ด ํ์ํ๋ค๋ ๊ฒ์ ์ ๊ทธ๋ฆฌ๊ฑฐํธ์ ์ค๊ณ๊ฐ ์๋ชป๋์์ ๊ฐ๋ฅ์ฑ์ด ์กด์ฌํ๋ค.
aggregate ์ value ๊ฐ ํ์ํ๋ฉด ํ๋ฒ์ load ๋์ด์ผ ํ๊ณ ํ์ํ์ง ์์ผ๋ฉด load ๋์ง ์์์ผ ํ๋ค๋ ์ด์ผ๊ธฐ๋ค.
๊ฒฐ๋ก
๊ฒฐ๋ก ์ ์ด์ผ๊ธฐํ ๋๊ฐ ๋์๋ค.
์ด์ด ์ข๊ฒ๋ ์ฐธ์ฌํ ํ๋ก์ ํธ ์ค์์ ๋๋ฉ์ธ์ ๋ถ๋ฆฌํ๋ ํ๋ก์ ํธ๊ฐ ์๊ณ ๋ถ๋ฆฌํ์ง ์์๋ ํ๋ก์ ํธ๊ฐ ์๋ค.
๋๋ฉ์ธ์ ๋ถ๋ฆฌํ๋ ํ๋ก์ ํธ์์๋ ์ ๋ง ๋๋ฉ์ธ์ ๋๋ฉ์ธ๋ต๊ฒ ์ฌ์ฉํ ์ ์๋๋ผ
์ฒ์ ํ๋ก์ ํธ ์งํ ๊ธฐ๊ฐ์ค 3/4 ์ fully ๋๋ฉ์ธ์ ์ง์คํ๊ณ 1/4 ์ ๊ธฐ๊ฐ๋์๋ง ์ค์ ๊ตฌํ์ ๋ํ ๊ณ ๋ฏผ์ ํ๊ณ ์ฝ๋๋ฅผ ์ณ๋ด๋ ค๊ฐ๋ค.
๋๋ฉ์ธ์ ๊ฐ๋ฐํ ๋๋ ์ ํ ์ฑ๋ฅ๊ณผ DB ํ ์ด๋ธ์ ํ๋, ์นผ๋ผ์ ๊ณ ๋ คํ์ง ์์๋ค.
๋จ์ ์ ๋ณด์๋ฉด ์ธํ๋ผ๋ฅผ ๊ตฌํํ ๋ ์ญ์ convert ํ๋๋ฐ ๋ง์ ์๊ฐ์ ์๋นํ๊ณ , ์ฌ๋ฌ DB ์ ์ ์ฝ ์กฐ๊ฑด (์ด๋ฅผํ ๋ฉด ๋๊ด์ ์ ๊ธ์ผ๋ก ์ธํ Version ์ ๋ํ ์ฒ๋ฆฌ) ๋๋ฌธ์ ๋๋ฌด๋ ๋นํฉ๋ฆฌ์ ์ธ ์ฝ๋๋ ์กด์ฌํ๊ธฐ๋ ํ์๋ค.
ํ์ง๋ง ์ค์ ๊ตฌํ๋ ํ๋์ฝ๋๋์ด๋ ๋ฌธ์ ๊ฐ ์์๋ค. ์ฑ๋ฅ์ด ์ข์ง ์์๋ ๋ฌธ์ ๊ฐ ์๋ค.
DIP ๋ฅผ ํด๋๊ธฐ ๋๋ฌธ์ ์ธ์ ๋ ์ง ๋ณ๊ฒฝํ ์ ์๋ ์์ ์ด ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋ํ Command Side ์ Query Side ์ ๋ถ๋ฆฌ๊ฐ ์๋ CQRS ๋ ํฌ๊ฒ ๋ฌธ์ ์๋ค. command ๋ query ๋ ์์์ ๋๋ฉ์ธ์ด๋ค
๋๋ฉ์ธ์ ๋ถ๋ฆฌํ์ง ์์๋ ํ๋ก์ ํธ์์๋ ๋น ๋ฅธ ์๋๊ฐ์ด ๊ด๊ฑด์ด์๋ค.
์ต๋ฒ๊ท ๋์ ๋๋ฉ์ธ ์ฃผ๋ ๊ฐ๋ฐ ์์ํ๊ธฐ: DDD ํต์ฌ ๊ฐ๋
์ ๋ฆฌ๋ถํฐ ๊ตฌํ๊น์ง
์์๋ '์ค์ ๋ก DB ๊ตฌํ ๊ธฐ์ ์ด ๋ฐ๋๋ ์ผ์ ์ค๋ฌด์์ ๊ฑฐ์ ์๋ค' ๋ผ๊ณ ๊น์ง ํํํ๋ค.
์ฌ์ค ์ต๊ทผ์๋ ํ์ฌ์์ DB ๊ตฌํ ๊ธฐ์ ์ด ๋ฐ๋๋ ๊ฒฝํ์ ํ๊ธฐ์ ์์ ๋ง์ 100% ๊ณต๊ฐ์ ํ์ง๋ ์์ง๋ง ๋ช๊ฐ์ง ๊ตฌํ ๊ธฐ์ ์ ๋ํ ์ด๋ ธํ ์ด์ ์ด ์นจํฌํ์ง๋ง ๋ญ ์ด๋ค๊ฐ?
์ฐ๋ฆฌ๋ ์ด๋ํฐ๋ผ๋ ๊ฒ์ ์๊ณ ์๊ณ ๋ฉ์ง ๋ค๋ฅธ ๋ฐฉ๋ฒ๋ค๋ ๋ฌด์ํ ๋ง๊ณ ์ญ์ ์ธ์ ๋ ๋ต์ด ์์ ๊ฒ์ด๋ค.
๊ตฌํ ๊ธฐ์ ์ ๋๋ฉ์ธ์์ ๊ฑท์ด๋ด๋ ์๋์ ์ฒ์๋ถํฐ ๋ถ๋ฆฌ์์ผ์ ์์ ์ ์งํํ๋ ๊ฒ ์ฌ์ด์์ ์ ์ ํ ๊ณ ๋ฏผ์ด ํ์ํด ๋ณด์ธ๋ค.
๊ทธ๋์ ๋ฌด์กฐ๊ฑด์ ์ธ Domain ๊ณผ ๊ตฌํ ๊ธฐ์ ์ ๋ถ๋ฆฌํด์ผํด! ๋ถ๋ฆฌํ๋ฉด ์ข๊ณ ๋ถ๋ฆฌํ์ง ์์ผ๋ฉด ์์ข์ ๋ผ๋ ์ด๋ถ๋ฒ์ ์ธ ์ฌ๊ณ ๋ ์ข์ง ์๋ค.
์ ์ ํ ๊ธฐ์ ๊ณผ ์ํฉ์ ๊ณ ๋ คํ์
'๐ฌweb application > - DDD' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
DDD ์ aggregate ์ ๋ํ ์ด์ผ๊ธฐ (1) | 2024.04.10 |
---|---|
[DDD] Value Object ์ ๋ํ ์ฌ๋ฌ ์ด์ผ๊ธฐ (0) | 2023.07.23 |
[DDD] Entity ์ ์๋ณ์(identifier) ์ ๋ํ ์ฌ๋ฌ ์ด์ผ๊ธฐ (2) | 2023.07.15 |
[DDD] Entity ์ ๋ํ ์ฌ๋ฌ ์ด์ผ๊ธฐ (0) | 2023.07.15 |
[DDD] Repository Pattern ์ด๋, ์ด๋ก ํธ (0) | 2022.08.23 |
๋๊ธ