๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
  • ์žฅ์›์ต ๊ธฐ์ˆ ๋ธ”๋กœ๊ทธ
๐Ÿ”ฌweb application/- DDD

Repository Pattern - ์‹ค์ „ํŽธ (Spring ์—์„œ DIP ๋ฅผ ํ†ตํ•ด Repository ์˜ ์„ ์–ธ๊ณผ ๊ตฌํ˜„ ๋ถ„๋ฆฌ์‹œํ‚ค๊ธฐ)

by Wonit 2022. 8. 28.

 

์ด ๊ธ€์€ ์ด๋ก ๊ณผ ์‹ค์Šต, ๋‘ ํŒŒํŠธ๋กœ ๋‚˜๋‰˜์–ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.

 

  1. Repository Pattern ์— ๋Œ€ํ•ด์„œ, ์ด๋ก ํŽธ
  2. 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. ๋ณต์žก์„ฑ
  2. ํ™•์žฅ์„ฑ

 

ํ•˜๋‚˜์”ฉ ํ™•์ธํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

 

๋ฌธ์ œ์  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 ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์–ด๋–ค๊ฐ€ ์ดํ•ด๊ฐ€ ์กฐ๊ธˆ ๋˜๋Š”๊ฐ€?

 

๋ถ„๋ฆฌ๋ฅผ ํ•จ์œผ๋กœ์จ ์šฐ๋ฆฌ๋Š” ๋ณต์žก์„ฑ์„ ๋‚ฎ์ถ”๊ณ  ํ™•์žฅ์„ฑ์„ ๋†’์ด๋Š” ์ด์ ์„ ์ทจํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋งˆ๋ƒฅ ์ข‹์€ ๊ฒƒ๋งŒ์€ ์•„๋‹ˆ๋‹ค.

 

๋ช‡๊ฐ€์ง€ ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๋ฌธ์ œ์ ๋“ค์ด ์žˆ๋Š”๋ฐ, ํ™•์ธํ•ด๋ณด์ž

 

ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๋ฌธ์ œ

 

ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๋ฌธ์ œ๋“ค์ด ๊ฝค๋‚˜ ์žˆ๋‹ค.

 

  1. ๋„ˆ๋ฌด ๋งŽ์€ ์ปจ๋ฒ„ํŒ… ์ฝ”๋“œ
  2. ํœด๋จผ ์—๋Ÿฌ
  3. 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 ๊ณผ ๊ตฌํ˜„ ๊ธฐ์ˆ ์„ ๋ถ„๋ฆฌํ•ด์•ผํ•ด! ๋ถ„๋ฆฌํ•˜๋ฉด ์ข‹๊ณ  ๋ถ„๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด ์•ˆ์ข‹์•„ ๋ผ๋Š” ์ด๋ถ„๋ฒ•์ ์ธ ์‚ฌ๊ณ ๋Š” ์ข‹์ง€ ์•Š๋‹ค.

 

์ ์ ˆํ•œ ๊ธฐ์ˆ ๊ณผ ์ƒํ™ฉ์„ ๊ณ ๋ คํ•˜์ž

๋Œ“๊ธ€