CHAP01 - DDD-START/ONLINE-STUDY GitHub Wiki

  • 1~2์žฅ: ๋„๋ฉ”์ธ ๋ชจ๋ธ์ด ๋ฌด์—‡์ธ์ง€

  • ๋‹ค๋ฃฐ ๋‚ด์šฉ

    • ๋„๋ฉ”์ธ ๋ชจ๋ธ
    • ์•คํ‹ฐํ‹ฐ์™€ ๋ฐธ๋ฅ˜
    • ๋„๋ฉ”์ธ ์šฉ์–ด

CH01 ๋„๋ฉ”์ธ ๋ชจ๋ธ์˜ ์‹œ์ž‘

๋„๋ฉ”์ธ

  • ์ฑ… ๊ตฌ๋งค
    • ๋ชฉ์ฐจ์™€ ํ‰๊ฐ€ ๋ณด๊ธฐ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ๋‹ด๊ธฐ, ์ฟ ํฐ์ฐพ๊ธฐ, ์นด๋“œ๊ฒฐ์ œ/๊ฐ€์ƒ๊ณ„์ขŒ์ž…๊ธˆ, ๋ฐฐ์†ก์ถ”์ 
  • ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ์˜จ๋ผ์ธ์„œ์ ์€ ์†Œํ”„ํŠธ์›จ์–ด๋กœ ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ•˜๋Š” ๋„๋ฉ”์ธ ์˜์—ญ์ด ๋œ๋‹ค.
  • ๋„๋ฉ”์ธ์€ ๋‹ค์‹œ ํ•˜์œ„๋„๋ฉ”์ธ์œผ๋กœ
    • ์ฃผ๋ฌธ
      • ๊ฒฐ์ œ, ๋ฐฐ์†ก, ํ˜œํƒ, etc
  • (๊ฒฝ์šฐ์— ๋”ฐ๋ผ) ์™ธ๋ถ€ ์‹œ์Šคํ…œ ์ด์šฉ
    • ๊ฒฐ์ œ - ์™ธ๋ถ€ PG
    • ๋ฐฐ์†ก - ์™ธ๋ถ€ ๋ฌผ๋ฅ˜
    • ์ฆ‰, ๋„๋ฉ”์ธ ๋ฐ‘์— ์„œ๋ธŒ๋„๋ฉ”์ธ์ด ํ•„์š”์— ๋”ฐ๋ผ ๋ณ€๊ฒฝ์ด ๋  ์ˆ˜๋„

๋„๋ฉ”์ธ ๋ชจ๋ธ

  • ํŠน์ • ๋„๋ฉ”์ธ์„ ๊ฐœ๋…์ ์œผ๋กœ ํ‘œํ˜„ํ•œ ๊ฒƒ.

    • UML์ด๋“  ๊ฐ์ฒด ๊ด€๊ณ„๋„๋กœ ํ‘œํ˜„ํ•˜๋“  ๊ฐœ๋…์ ์œผ๋กœ๋งŒ ํ‘œํ˜„ํ•˜๋ฉด ๋œ๋‹ค. (์•„๋ž˜ ๊ทธ๋ฆผ)
    • ์‚ฌ๋ก€: ์ฃผ๋ฌธ ๊ณผ์ •
      • ์ฃผ๋ฌธ์ˆ˜๋Ÿ‰ ์„ค์ • -> ๋ฐฐ์†ก์ง€ ์ž…๋ ฅ -> ๊ฒฐ์ œ์ˆ˜๋‹จ ์„ ํƒ ํ›„ ๊ณ„์‚ฐ -> ์ฃผ๋ฌธ ์™„๋ฃŒ -> ์ถœ๋ฐœ์•ˆํ–ˆ์Œ, ๋ฐฐ์†ก์ง€ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ

    image-20211223110703141

  • ๊ฐ์ฒด ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋„๋ฉ”์ธ์„ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์ดํ•ด์™€ ์ง€์‹ ๊ณต์œ  ๋„์›€๋˜๋ฉด ๋‹ค๋ฅธ ๋Œ€์ฒด์ œ๋„ ๋ชจ๋‘ ๊ฐ€๋Šฅํ•˜๋‹ค.

    • e.g. ์ƒํƒœ ๋‹ค์ด์–ด๊ทธ๋žจ (์•„๋ž˜ ํ™•์ธ)
      • ๊ฒฐ์ œ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์ƒํ’ˆ ์ค€๋น„
      • ์ถœ๊ณ ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ์ทจ์†Œ ๋ถˆ๊ฐ€

image-20211223111133178

๋„๋ฉ”์ธ ๋ชจ๋ธ ํŒจํ„ด

  • ๊ฐ€์žฅ ํ”ํ•œ Application ์•„ํ‚คํ…์ฒ˜
    • 4 ๊ณ„์ธต: ํ‘œํ˜„ - ์‘์šฉ - ๋„๋ฉ”์ธ - ์ธํ”„๋ผ(DB) (Layer Architecture ๋งํฌ)
      • ํ‘œํ˜„ - UI. ์‚ฌ์šฉ์ž/์™ธ๋ถ€์‹œ์Šคํ…œ ์š”์ฒญ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋ณด์—ฌ์ค€๋‹ค.
      • ์‘์šฉ - ์‚ฌ์šฉ์ž ์š”์ฒญ ๊ธฐ๋Šฅ ์‹คํ–‰. ์—…๋ฌด ๋กœ์ง X. ๋„๋ฉ”์ธ ๊ณ„์ธต ์กฐํ•ฉํ•ด์„œ ๊ธฐ๋Šฅ ์‹คํ–‰.
      • ๋„๋ฉ”์ธ - ๋„๋ฉ”์ธ ๊ทœ์น™ ๊ตฌํ˜„.
      • ์ธํ”„๋ผ - DB๋‚˜ ๋ฉ”์‹œ์ง• ์™ธ๋ถ€ ์‹œ์Šคํ…œ ์—ฐ๋™ํ•˜์—ฌ ์ฒ˜๋ฆฌ
    • ์˜ˆ์ธก: ์ฃผ๋ฌธ- ์ฃผ๋ฌธ, ๊ณ„์‚ฐ, ๋ฐฐ์†ก
  • ์–ด๋–ค ์ฝ”๋“œ๊ฐ€ ๋„๋ฉ”์ธ ๊ณ„์ธต์—?
    • ์‚ฌ๋ก€: ์ฃผ๋ฌธ ๋„๋ฉ”์ธ ๊ทœ์น™
      • ๊ทœ์น™1: ์ถœ๊ณ  ์ „์— ๋ฐฐ์†ก์ง€ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
      • ๊ทœ์น™2: ์ฃผ๋ฌธ ์ทจ์†Œ๋Š” ๋ฐฐ์†ก์ „์—๋งŒ ๊ฐ€๋Šฅ
      • ์•„๋ž˜๋Š” ๋ฐ˜์˜ ์ฝ”๋“œ
  • ํฌ์ธํŠธ: ์ฃผ๋ฌธ๊ณผ ๊ด€๋ จ๋œ ์ค‘์š”ํ•œ ์—…๋ฌด ๊ทœ์น™์„ ์ฃผ๋ฌธ ๋„๋ฉ”์ธ ๋ชจ๋ธ์ธ ์•„๋ž˜ 2๊ฐœ์˜ ๊ฐ์ฒด์—์„œ ํ•˜๊ณ  ์žˆ๋‹ค๋Š”๊ฒƒ.
public class Order {
    private OrderState state;
    private ShippingInfo shippingInfo;
    public void changeShippingInfo(ShippingInfo newShippingInfo) { // ๊ทœ์น™1: ๋ฐฐ์†ก์ง€ ๋ณ€๊ฒฝ
        if (!state.isShippingChangeable()) {
            throw new IllegalStateException("canโ€™t change shipping in" + state);
        }
        this.shippingInfo = newShippingInfo;
    }
    public void changeShipped() {
        // ๋กœ์ง ๊ฒ€์‚ฌ 
        this.state = OrderState.SHIPPED;
    }
}

public enum OrderState {
    PAYMENT_WAITING { // ๊ฒฐ์ œ๋Œ€๊ธฐ 
        public boolean isShippingChangeable() {
            return true;
        }
    }
    PREPARING { // ์ƒํ’ˆ์ค€๋น„์ค‘
        public boolean isShippingChangeable() {
            return true;
        }
    },
    SHIPPED, DELIVERING, DELIVERY_COMPLETED; // ์„ ์ ์ค‘, ๋ฐฐ์†ก์ค‘, ๋ฐฐ์†ก์™„๋ฃŒ
    public boolean isShippingChangeable() {
        return false;
    }
}
  • ๋ฆฌํŒฉํ† ๋ง: OrderState๋Š” Order์— ์†ํ•œ ๋ฐ์ดํ„ฐ์ด๋‹ค. ์ฆ‰, ๋ฐฐ์†ก์ง€ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํŒ๋‹จ ์ฝ”๋“œ๋Š” Order๋กœ ์ด๋™ ๊ฐ€๋Šฅ.
public class Order {
    private OrderState state;
    private ShippingInfo shippingInfo;
    public void changeShippingInfo(ShippingInfo newShippingInfo) {
        if (!isShippingChangeable()) {
            throw new IllegalStateException("canโ€™t change shipping in" + state);
        }
        this.shippingInfo = newShippingInfo;
    }
    
    private boolean isShippingChangeable() {
        return state == OrderState.PAYMENT WAITING ||
            state == OrderState.PREPARING;
    }
}
public enum OrderState {
    PAYMENT_WAITING, PREPARING, SHIPPED, DELIVERING, DELIVERY_COMPLETED;
}

๊ฐœ๋…๋ชจ๋ธ vs ๊ตฌํ˜„๋ชจ๋ธ

  • ๋ฌธ์ œ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ๊ฐœ๋… ๋ชจ๋ธ๋กœ ๊ฒฐ๊ณผ๋ฌผ์„ ๋ฝ‘์•„๋‚ธ ๋’ค, ๊ตฌํ˜„ ๋ชจ๋ธ๋กœ ์ „ํ™˜ํ•œ๋‹ค.

๋„๋ฉ”์ธ ๋ชจ๋ธ ๋„์ถœ

  • ๋ชจ๋ธ ๋„์ถœํ•˜๊ธฐ ์œ„ํ•ด์„ , ๊ธฐํš์„œ, USE CASE, ์‚ฌ์šฉ์ž ์Šคํ† ๋ฆฌ๋กœ ์ดˆ์•ˆ ๋งŒ๋“ค๊ธฐ.

  • ๋ชจ๋ธ๋ง ๊ธฐ๋ณธ ์ž‘์—…๋“ค

    • ํ•ต์‹ฌ ๊ตฌ์„ฑ ์š”์†Œ
    • ๊ทœ์น™
    • ๊ธฐ๋Šฅ
  • ์ฃผ๋ฌธ ๊ด€๋ จ ์š”๊ตฌ์‚ฌํ•ญ

    1. ์ตœ์†Œ ํ•œ ์ข…๋ฅ˜ ์ด์ƒ์˜ ์ƒํ’ˆ์„ ์ฃผ๋ฌธํ•ด์•ผ ํ•œ๋‹ค .
    2. ํ•œ ์ƒํ’ˆ์„ ํ•œ๊ฐœ ์ด์ƒ ์ฃผ๋ฌธํ•  ์ˆ˜ ์žˆ๋‹ค.
    3. ์ด ์ฃผ๋ฌธ ๊ธˆ์•ก์€ ๊ฐ ์ƒํ’ˆ์˜ ๊ตฌ๋งค ๊ฐ€๊ฒฉํ•ฉ์„ ๋ชจ๋‘ ๋”ํ•œ ๊ธˆ์•ก ์ด๋‹ค .
    4. ๊ฐ ์ƒํ’ˆ์˜ ๊ตฌ๋งค ๊ฐ€๊ฒฉ ํ•ฉ์€ ์ƒํ’ˆ ๊ฐ€๊ฒฉ์— ๊ตฌ๋งค ๊ฐœ์ˆ˜๋ฅผ ๊ณฑํ•œ ๊ฐ’ ์ด๋‹ค .
    5. ์ฃผ๋ฌธํ•  ๋•Œ ๋ฐฐ์†ก์ง€ ์ •๋ณด๋ฅผ ๋ฐ˜๋“œ์‹œ ์ง€์ •ํ•ด์•ผ ํ•œ ๋‹ค .
    6. ๋ฐฐ์†ก์ง€ ์ •๋ณด๋Š” ๋ฐ›๋Š” ์‚ฌ๋žŒ ์ด๋ฆ„, ์ „ํ™”๋ฒˆํ˜ธ, ์ฃผ์†Œ๋กœ ๊ตฌ์„ฑ ๋œ๋‹ค .
    7. ์ถœ๊ณ ๋ฅผ ํ•˜๋ฉด ๋ฐฐ์†ก์ง€ ์ •๋ณด๋ฅผ ๋ณ€๊ฒฝ ํ•  ์ˆ˜ ์—†๋‹ค .
    8. ์ถœ๊ณ  ์ „์— ์ฃผ๋ฌธ์„ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋‹ค .
    9. ๊ณ ๊ฐ์ด ๊ฒฐ์ œ๋ฅผ ์™„๋ฃŒ ํ•˜๊ธฐ ์ „์—๋Š” ์ƒํ’ˆ์„ ์ค€๋น„ ํ•˜์ง€ ์•Š๋Š”๋‹ค .
  • ์ด๋กœ ์•Œ ์ˆ˜ ์žˆ๋Š” 4๊ฐ€์ง€ ๊ธฐ๋Šฅ

    • ์ด ๊ธฐ๋Šฅ๋“ค์„ ๋ฉ”์†Œ๋“œ๋กœ ์ถ”๊ฐ€ํ•  ๊ฒฝ์šฐ

    • public class Order {
          // 1 ์ถœ๊ณ  ์ƒํƒœ๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ -> 8 
          public void changeShipped() { .. } 
          // 2 ๋ฐฐ์†ก์ง€ ์ •๋ณด ๋ณ€๊ฒฝํ•˜๊ธฐ
          public void changeShippingInfo(ShippingInfo newShipping) { .. }
          // 3 ์ฃผ๋ฌธ ์ทจ์†Œํ•˜๊ธฐ
          public void cancel() { .. }
          // 4 ๊ฒฐ์ œ์™„๋ฃŒ๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ
          public void completePayment() { .. }
      }
  • 2, 4 ์š”๊ตฌ์‚ฌํ•ญ์— ์˜ํ•ด ์ฃผ๋ฌธ ํ•ญ๋ชฉ์ด ์–ด๋–ค ๋ฐ์ดํ„ฐ๋กœ ๊ตฌ์„ฑ๋  ์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

    • OrderLine ๊ฐ์ฒด ์ฐธ๊ณ 
public class OrderLine {
    private Product product;
    private int price; // ๊ฐœ๋‹น ๊ฐ€๊ฒฉ
    private int quantity; // ์ƒํ’ˆ ๊ฐœ์ˆ˜
    private int amounts; // ์ด๊ธˆ์•ก
    public OrderLine(Product product, int price, int quantity) {
        this.product = product;
        this.price = price;
        this.quantity = quantity;
        this.amounts = calculateAmounts();
    }
    private int calculateAmounts() { // ์ด ๊ธˆ์•ก ๊ณ„์‚ฐ 
        return price * quantity;
    }
    public int getAmounts() { ... }
}
  • ์š”๊ตฌ์‚ฌํ•ญ 1๊ณผ 3์€ Order์™€ OrderLine์˜ ๊ด€๊ณ„๋ฅผ ๋ณด์—ฌ์ค€๋‹ค. ๋”ฐ๋ผ์„œ Order๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝ.
public class Order {
    private List<OrderLine> orderLines;
    private int totalAmounts;
    public Order(List<OrderLine> orderLines) {
        setOrderLines(orderLines);
    }
    private void setOrderLines(List<OrderLine> orderLines) {
        verifyAtLeastOneOrMoreOrderLines(orderLines); // ์š”๊ตฌ์‚ฌํ•ญ 1: ์ตœ์†Œ 1๊ฐœ ์ด์ƒ
        this.orderLines = orderLines;
        calculateTotalAmounts(); // ์š”๊ตฌ์‚ฌํ•ญ 3: ์ด ์ฃผ๋ฌธ ํ•ฉ๊ณ„ ๊ณ„์‚ฐ
    }
    private void verifyAtLeastOneOrMoeOrderLines(List<OnderLine> orderLines) {
        if (orderLines == null || orderLines.isEmpty()) {
            throw new IllegalArgumentException("no OrderLine");
        }
    }
    private void calculateTotalAmounts() {
        this.totalAmounts = new Money(orderLines.stream()
                                      .mapToInt(x ->
                                                x.getAmounts().getValue()).sum());
    }
    //... ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ
}
  • ShippingInfo
public class ShippingInfo {
    private String receiverName;
    private String receiverPhoneNumber;
    private String shippingAddress1;
    private String shippingAddress2;
    private String shippingZipcode;
    //... ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ
}
  • ์š”๊ตฌ์‚ฌํ•ญ 5์— ๋”ฐ๋ผ, Order๋ฅผ ์ƒ์„ฑ ํ• ๋•Œ OrderLine ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ShippingInfo๋„ ํ•จ๊ป˜ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.
  • ์ƒ์„ฑ์ž์— ๋ฐ˜์˜
public class Order {
    private List<OrderLine> orderLines;
    private int totalAmounts;
    public Order(List<OrderLine> orderLines, ShippingInfo shippingInfo) {
        setOrderLines(orderLines);
        setShippingInfo(shippingInfo); // ์š”๊ตฌ์‚ฌํ•ญ5: ๋ฐฐ์†ก์ง€ ์•ˆ์ ์œผ๋ฉด ์—๋Ÿฌ! = ํ•„์ˆ˜
    }

    private void setShippingInfo(ShippingInfo shippingInfo){
        if(shippingInfo == null) 
            throw new IllegalArguamentException("no shipping info")
            this.shippingInfo = shippingInfo;
    }
    //... ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ
}
  • ์š”๊ตฌ์‚ฌํ•ญ 7, 8 (9๋ฒˆ๋„ ๊ด€๋ จ)
    • Enum ์ด์šฉํ•˜์—ฌ ์ƒํƒœ๋กœ ํ‘œํ˜„ํ•˜๊ธฐ
public enum OrderState {
    PAYMENT_WAITING, PREPARING, SHIPPED, DELIVERING, DELIVERY_COMPLETED;
}
  • Order์— ํ•ด๋‹น ์š”๊ตฌ์‚ฌํ•ญ์„ ์ ์šฉํ•˜๋ฉด,
    • ์ฃผ๋ชฉ: isShippingChangeable -> verifyNotYetShipped
      • ์‹œ๊ฐ„์ด ํ๋ฅด๋ฉฐ ๋„๋ฉ”์ธ์„ ๋” ์ž˜ ์•Œ์•˜๊ธฐ์—, ๋ฆฌํŒฉํ† ๋ง
        • ๋ฐฐ์†ก์ง€ ์ •๋ณด ๋ณ€๊ฒฝ์™€ ์ฃผ๋ฌธ์ทจ์†Œ๊ฐ€ "์ถœ๊ณ ์ „์— ๊ฐ€๋Šฅ" ์ด๋ผ๋Š” ์ œ์•ฝ์กฐ๊ฑด์„ ์ฐพ๊ฒŒ๋˜์—ˆ๊ธฐ์—~
public class Order {
    private OrderState state;
    public void changeShippingInfo(ShippingInfo newShippingInfo) {
        verifyNotYetShipped();
        setShippingInfo(newShippingInfo);
    }
    public void cancel() {
        verifyNotYetShipped();
        this.state = OrderState.CANCELED;
    }
    private void verifyNotYetShipped() { // isShippingChangeable์—์„œ ๋ณ€๊ฒฝ 
        if (state != OrderState.PAYMENT_WAITING & state != OrderState.PREPARING)
            throw new IllegalStateException("aleady shipped");
    }
}

Entity์™€ Value

  • ๋„์ถœํ•œ ๋ชจ๋ธ์€ ์—”ํ‹ฐํ‹ฐ์™€ ๋ฐธ๋ฅ˜๋กœ ๊ตฌ๋ถ„ ๊ฐ€๋Šฅ
  • ๋„๋ฉ”์ธ ๊ตฌํ˜„์„ ์œ„ํ•ด, ๋‘˜์˜ ์ฐจ์ด๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.
  • ์—ฌ๊ธฐ์„  Value๋ฅผ '๊ฐ’'์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„๊ฒƒ.

![image-20211223180521665](D:\0 Google Drive\03 ์Šคํ„ฐ๋”” ๋ชจ์ž„\DDD\1์ฃผ์ฐจ ๋ฐœํ‘œ.assets\image-20211223180521665.png)

์—”ํ‹ฐํ‹ฐ

  • ์‹๋ณ„์ž๋ฅผ ๊ฐ–๋Š”๋‹ค.

    • e.g. ์ฃผ๋ฌธ๋ฒˆํ˜ธ (orderNumber)
    • ๋ฐฐ์†ก์ง€๊ฐ€ ๋ฐ”๊ปด๋„ ์ฃผ๋ฌธ๋ฒˆํ˜ธ๊ฐ€ ๋ฐ”๋€Œ์ง€ ์•Š์œผ๋ฏ€๋กœ, ์‹๋ณ„์ž๋Š” ๋ณ€๊ฒฝ x. ์ฆ‰, ์‚ญ์ œ๋ ๋•Œ ๊นŒ์ง€ ์œ ์ง€.
  • equals์™€ hashCode๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

  • public class Order {
        private String orderNumber;
        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj== null) return false;
            if (obj.getClass() != Order.class) return false;
            Order other = (Order)obj;
            if (this.orderNumber == null) return false;
            return this.orderNumber.equals(other.orderNumber);
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((orderunber = null) ? @ : orderNurber.hashCode());
            return result;
        }
    }

์—”ํ‹ฐํ‹ฐ์˜ ์‹๋ณ„์ž ์ƒ์„ฑ

  1. ํŠน์ • ๊ทœ์น™์— ๋”ฐ๋ผ ์ƒ์„ฑ
  2. UUID
  3. ๊ฐ’ ์ง์ ‘ ์ž…๋ ฅ
  4. ์ผ๋ จ๋ฒˆํ˜ธ ์‚ฌ์šฉ
  • ํŠน์ • ๊ทœ์น™์— ๋”ฐ๋ผ ์ƒ์„ฑ

    • ์‚ฌ๋ก€: ์•„๋งˆ์กด/์•Œ๋ผ๋”˜ ์˜จ๋ผ์ธ์„œ์ 
      • ์ฃผ๋ฌธ๋ฒˆํ˜ธ, ์šด์†ก์žฅ๋ฒˆํ˜ธ, ์นด๋“œ๋ฒˆํ˜ธ๋Š” ํŠน์ • ๊ทœ์น™์— ๋”ฐ๋ผ ์ƒ์„ฑ
    • ํ”ํžˆ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
      • ์‹œ๊ฐ„๊ณผ ๋‹ค๋ฅธ ๊ฐ’์„ ํ•จ๊ป˜ ์กฐํ•ฉ
      • 2015๋…„ 05์›” 29์ผ 09์‹œ 46๋ถ„ 44์ดˆ
        • 20150529094644
      • ๊ฐ™์€ ์‹œ๊ฐ„์— ์ƒ์„ฑ์‹œ, ๊ฐ™์€ ์‹๋ณ„์ž ๋งŒ๋“ค์–ด์งˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.
  • UUID

    • e.g. java.util.UUID

    • UUID uuid  = UUID.randoUUID();
      String strUuid = uuid.toString();
  • ๊ฐ’ ์ง์ ‘ ์ž…๋ ฅ

    • e.g. ์‚ฌ์šฉ์ž ์•„์ด๋””๋‚˜ ์ด๋ฉ”์ผ
  • ์ผ๋ จ๋ฒˆํ˜ธ

    • e.g. ์‹œํ€€์Šค๋‚˜ DB์˜ ์ž๋™ ์ฆ๊ฐ€ ์ปฌ๋Ÿผ

      • String orderNumber = orderRepository.generate();
        Order order = new Order(orderNumber, ...);
        orderRepository.save(order);
    • ์ž๋™ ์ฆ๊ฐ€ ์ปฌ๋Ÿผ์˜ ๊ฒฝ์šฐ, ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•ด์•ผ ๊ฐ’์„ ์•Œ์ˆ˜ ์žˆ๊ธฐ์—, ๊ฐ์ฒด ์ƒ์„ฑ์‹œ ์‹๋ณ„์ž๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์—†๋‹ค.

      • Article article = new Article(author, title, ...);
        articleRepository.save(article);
        Lond savedArticleId = article.getId(); // ์ €์žฅ ํ›„ ์‹๋ณ„์ž ์ฐธ์กฐ ๊ฐ€๋Šฅ
    • 4์žฅ์—์„œ ์ž์„ธํžˆ

๋ฐธ๋ฅ˜ ํƒ€์ž…

  • ๋ฐธ๋ฅ˜ ํƒ€์ž… = ๊ฐœ๋…์ ์œผ๋กœ ์™„์ „ํ•œ ํ•˜๋‚˜๋ฅผ ํ‘œํ˜„
  • ์‚ฌ๋žŒ๊ณผ ์ฃผ์†Œ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ
// ๋ฐฐ์†ก์ง€ 
public class ShippingInfo {
    // ์•„๋ž˜ 2๊ฐœ - ๋ฐ›๋Š” ์‚ฌ๋žŒ์ด๋ผ๋Š” ํ•˜๋‚˜์˜ ๊ฐœ๋…
    private String receiverName;
    private String receiverPhoneNumber;
    
    // ์•„๋ž˜ 3๊ฐœ - ์ฃผ์†Œ๋ผ๋Š” ํ•˜๋‚˜์˜ ๊ฐœ๋…
    private String shippingAddressi;
    private String shippingAddress2;
    private String shippingZipcode;

    //... 
}
  • ๋ฐธ๋ฅ˜ ํƒ€์ž…์— ๋”ฐ๋ผ, ์ชผ๊ฐค ๊ฒฝ์šฐ
public class Receiver {
    private String name;
    private String phoneNumber;
    public Receiver(String name, String phoneNumber) {
        this.name = name;
        this.phoneNumber = phoneNumber;
    }
    public String getName() {
        return name;
    }
    public String getPhoneNumber() {
        return phoneNumber;
    }
}
public class Address{
    private String address1;
    private String address1;
    private String zipcode;
    //... 
}

๊ฒฐ๊ณผ์ ์œผ๋กœ ShippingInfo๋Š” ์ด๋ ‡๊ฒŒ ๋ฐ”๋€๋‹ค.

public class ShippingInfo{
    private Receiver receiver;
    private Address address;
}

๋˜ ๋‹ค๋ฅธ ์‚ฌ๋ก€๋กœ OrderLine์— ๋ฐธ๋ฅ˜ํƒ€์ž…์„ ํ™œ์šฉ ํ•ด๋ณด์ž.

// before
public class OrderLine {
    private Product product;
    private int price; // ๊ฐœ๋‹น ๊ฐ€๊ฒฉ
    private int quantity; // ์ƒํ’ˆ ๊ฐœ์ˆ˜
    private int amounts; // ์ด๊ธˆ์•ก
    //
}

// after
public class OrderLine {
    private Product product;
    private Money price; // ๊ฐœ๋‹น ๊ฐ€๊ฒฉ
    private int quantity; // ์ƒํ’ˆ ๊ฐœ์ˆ˜
    private Money amounts; // ์ด๊ธˆ์•ก
    //..
}

public class 1 {
    private int value;
    public Money(int value) {
        this.value = value;
    }
    private int getValue() { 
        return this.value;
    }
}

Moneyํƒ€์ž…์„ ์‚ฌ์šฉํ–ˆ๊ธฐ์— price๋‚˜ amount๊ฐ€ ๊ธˆ์•ก์„ ์˜๋ฏธํ•จ์„ ๋ช…ํ™•ํžˆ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

๋ฐธ๋ฅ˜ ํƒ€์ž…์˜ ์ถ”๊ฐ€์  ์žฅ์  - ๋ฐธ๋ฅ˜ ํƒ€์ž…์„ ์œ„ํ•œ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ.

// ์‚ฌ๋ก€ - ๋ˆ ๊ณ„์‚ฐ ๊ธฐ๋Šฅ
public class Money {
    private int value;
	// ...
    public Money add(Money money) {
        return new Money(this.value + money.value); // -> ํŠน์ง•: ๋ถˆ๋ณ€
    }
    public Money multiply(int multiplier) {
        return new Money(value * multiplier);
    }
}

์š”์•ฝ, ๋ฐธ๋ฅ˜ ํƒ€์ž… = ์ฝ”๋“œ์˜ ์˜๋ฏธ ๋ช…ํ™•ํ™” ํŠน์ง• - ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธด ๋ณด๋‹จ, ์ƒˆ๋กœ์šด ๊ฐ์ฒด ์ƒ์„ฑ ์„ ํ˜ธํ•˜๋Š” ํŽธ์ž„ ๋ถˆ๋ณ€ = ์•ˆ์ „ํ•œ ์ฝ”๋“œ ์ž‘์„ฑ ๊ฐ€๋Šฅ

Money price = ...;
OrderLine line  new OrderLine(product, price, quantity);
// ๋งŒ์•ฝ price.setValue(0)๋กœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด?

line๊ณผ price์˜ ๊ฐ’์ด ๋‹ค๋ฅด๋‹ค.

![image-20211223185550393](D:\0 Google Drive\03 ์Šคํ„ฐ๋”” ๋ชจ์ž„\DDD\1์ฃผ์ฐจ ๋ฐœํ‘œ.assets\image-20211223185550393.png)

๋ฐœ์ƒ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ ์ถ”๊ฐ€ ์กฐ์น˜๋กœ

public class OrderLine {
    private Money price;
    public OrderLine(Product product, Money price, int quantity) {
        this.product = product;
        this.price = new Money(price.getValue());
        this.quantity = quantity;
        this.amounts = calculateAmounts();
    }
}

๋‹ค๋งŒ, Money๊ฐ€ ๋ถˆ๋ณ€์ด์—ˆ๋‹ค๋ฉด, ์ด๋Ÿฐ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์—ˆ๋‹ค.

์—”ํ‹ฐํ‹ฐํƒ€์ž… ๊ฐ์ฒด ๋น„๊ต๋Š” ์‹๋ณ„์ž๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด, ๋‘ ๋ฐธ๋ฅ˜ ๊ฐ์ฒด๋Š” ๋ชจ๋“  ์†์„ฑ๊ฐ’๋“ค์„ ๋น„๊ตํ•œ๋‹ค.

public class Receiver { 
    private String name;
    private String phoneNumber;
    public boolean equals(Object other) {
        if (other == null) return false;
        if (this == other) return true; // ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๊ฐ’ ๋น„๊ต
        if (! (other instanceof Receiver) ) return false;
        Receiver that = (Receiver) other;
        return this.name.equals(that.name) &&
            this.phoneNumber.equals(that.phoneNumber)
    }
    // ...
}

์—”ํ‹ฐํ‹ฐ ์‹๋ณ„์ž์™€ ๋ฐธ๋ฅ˜ ํƒ€์ž…

  • ์‹๋ณ„์ž๋Š” ๋‹จ์ˆœํ•œ ๋ฌธ์ž์—ด์ด ์•„๋‹ˆ๋ผ ๋„๋ฉ”์ธ์—์„œ ํŠน๋ณ„ํ•œ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. (e.g. ์ฃผ๋ฌธ ๊ฐ์ฒด์˜ ์ฃผ๋ฌธ๋ฒˆํ˜ธ)
// before
private int orderNo;
// after
private OrderNo id; // -> ์˜๋ฏธ์ ์œผ๋กœ ๋” ์ž˜ํ‘œํ˜„๋œ๋‹ค.

๋„๋ฉ”์ธ ๋ชจ๋ธ์— setter ๋ฉ”์„œ๋“œ ๋„ฃ์ง€ ์•Š๊ธฐ

  • ๊ณ ์ณ์•ผํ• ๊ฒƒ: ์Šต๊ด€์  ์ถ”๊ฐ€ = set ๋ฉ”์„œ๋“œ
  • ์˜๋ฏธ์  ์ฐจ์ด
    • changeShippingInfo(ShippingInfo newShippingInfo): ๋ฐฐ์†ก์ง€ ์ •๋ณด ์ƒˆ๋กœ ๋ณ€๊ฒฝ
    • setShippingInfo(ShippingInfo shippingInfo): ๋ฐฐ์†ก์ง€ ๊ฐ’์„ ์„ค์ •
  • ์˜๋ฏธ์ ์œผ๋กœ ์˜ฌ๋ฐ”๋ฅธ ๋ฉ”์„œ๋“œ๋ช…์œผ๋กœ ์จ์•ผํ•œ๋‹ค.

set ์‚ฌ์šฉ์‹œ ๋ฌธ์ œ์ฝ”๋“œ

// ์ƒ์„ฑ์‹œ์ ์— ํ•„์š”ํ•œ ๊ฒƒ์„ ์ „๋‹ฌํ•ด์ฃผ์ง€ ์•Š๊ณ , set์„ ์ด์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋ ค๋‹ค. orderer๋ฅผ ๋†“์ณค๋‹ค.
Order order = new Order();
order.setOrderLine(lines);
order.setShippingInfo(shippingInfo);
order.setState(OrderState.PREPARING); //orderer๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ ์™„๋ฃŒ ์ฒ˜๋ฆฌ

๋” ๋‚˜์€ ์ฝ”๋“œ

  • ์ƒ์„ฑ์ž ํ˜ธ์ถœ ์‹œ์  ๋ฐ์ดํ„ฐ ์œ ํšจ ๊ฒ€์‚ฌ ๊ฐ€๋Šฅ
Order order = new Order(orderer, lines, shippingInfo, OrderState.PREPARING);

์™„์„ฑ ์ฝ”๋“œ

  • ์—ฌ๊ธฐ์„œ๋„ set ์‚ฌ์šฉํ•˜์ž–์•„์š”!?
    • ์ ‘๊ทผ ๋ฒ”์œ„๋ฅผ ๋ณด์ž: private = ๋ถˆ๋ณ€ ๋ฐธ๋ฅ˜ ํƒ€์ž…
public class Order {
    public Order(Orderer orderer, List<OrderLine> orderLines,
                 ShippingInfo shippingInfo, OrderState state) {
        setOrderer(orderer);
        setOrderLines(orderLines);
        // ...
    }
    private void setOrderer(Orderer orderer) { 
        if (orderer == null) throw new IllegalArgumentException("no orderer");
        this.orderer = orderer;
    }
    private void setOrderLines(List<OrderLine> orderLines) { 
        verifyAtLeastOneOrMoreOrderLines(orderLines);
        this.orderLines = orderLines;
        calculateTotalAmounts();
    }
    private void verifyAtLeastOneOrMoreOrdert ines(List<Orderiine> orderiines) {
        if (orderLines == null || orderLines.isEmpty()) {
            โ€˜throw new IllegalArgumentException("no OrderLine");
        }
    }
    private void calculateTotalAmounts() {
        this.totalAmunts = ordertines.stream() .mepToInt(x -> X.getAmounts()).sum();
    }
}

๋„๋ฉ”์ธ ์šฉ์–ด

  • ๋„๋ฉ”์ธ ์šฉ์–ด๋Š” ์ค‘์š”ํ•˜๊ธฐ์— ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š”๋ฐ ์ด๋ฅผ ์ด์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ฒฐ์ œ๋Œ€๊ธฐ์ค‘, ์ƒํ’ˆ์ค€๋น„์ค‘, ์ถœ๊ณ ์™„๋ฃŒ, ๋ฐฐ์†ก์ค‘, ๋ฐฐ์†ก์™„๋ฃŒ, ์ฃผ๋ฌธ์ทจ์†Œ
    • ๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ํ‘œํ˜„ํ•˜๋ฉด?
public enum OrderState {
	STEP1, STEP2, STEP3, STEP4, STEP5, STEP6
}
  • ๋„๋ฏธ๋…ธ ์ฒ˜๋Ÿผ
    • ๋‹ค๋ฅธ ์ฝ”๋“œ๋„ ์ด์ƒํ•ด ์ง„๋‹ค
// ๊ทœ์น™์ด ๋“ค์–ด๋‚˜์ง€ X
public class Order {
    public void changeShippingInfo(ShippingInfo newShippingInfo) {
        verifyStep1OrStep2(); // verifyNotYetShipped
        setShippingInfo(newShippingInfo);
    }
    private void verifyStep1OrStep2() {
        // ๊ฒฐ์ œ๋Œ€๊ธฐ์ค‘, ์ƒํ’ˆ์ค€๋น„์ค‘ ์˜๋ฏธ ์•ˆ๋“œ๋Ÿฌ๋‚จ
        if (state != OrderState.STEP1 && state != OrderState.STEP2)
            throw new IllegalStateException("aleady shipped");
    }
}
  • ์˜ฌ๋ฐ”๋ฅธ ๋‹จ์–ด ์„ ํƒ: state vs status, kind vs type
  • ์–ด๋ ต์ง€๋งŒ, ์‹œ๊ฐ„ ๋“ค์—ฌ ์ฐพ์•„ ์ ์ ˆํ•œ ๋‹จ์–ด๋ฅผ ๊ณ ๋ฅด์ž.
โš ๏ธ **GitHub.com Fallback** โš ๏ธ