CHAP01 - DDD-START/ONLINE-STUDY GitHub Wiki
-
1~2์ฅ: ๋๋ฉ์ธ ๋ชจ๋ธ์ด ๋ฌด์์ธ์ง
-
๋ค๋ฃฐ ๋ด์ฉ
- ๋๋ฉ์ธ ๋ชจ๋ธ
- ์คํฐํฐ์ ๋ฐธ๋ฅ
- ๋๋ฉ์ธ ์ฉ์ด
- ์ฑ
๊ตฌ๋งค
- ๋ชฉ์ฐจ์ ํ๊ฐ ๋ณด๊ธฐ, ์ฅ๋ฐ๊ตฌ๋๋ด๊ธฐ, ์ฟ ํฐ์ฐพ๊ธฐ, ์นด๋๊ฒฐ์ /๊ฐ์๊ณ์ข์ ๊ธ, ๋ฐฐ์ก์ถ์
- ์ด๋ฌํ ๊ธฐ๋ฅ์ ๊ฐ์ง ์จ๋ผ์ธ์์ ์ ์ํํธ์จ์ด๋ก ํด๊ฒฐํ๊ณ ์ ํ๋ ๋๋ฉ์ธ ์์ญ์ด ๋๋ค.
- ๋๋ฉ์ธ์ ๋ค์ ํ์๋๋ฉ์ธ์ผ๋ก
- ์ฃผ๋ฌธ
- ๊ฒฐ์ , ๋ฐฐ์ก, ํํ, etc
- ์ฃผ๋ฌธ
- (๊ฒฝ์ฐ์ ๋ฐ๋ผ) ์ธ๋ถ ์์คํ
์ด์ฉ
- ๊ฒฐ์ - ์ธ๋ถ PG
- ๋ฐฐ์ก - ์ธ๋ถ ๋ฌผ๋ฅ
- ์ฆ, ๋๋ฉ์ธ ๋ฐ์ ์๋ธ๋๋ฉ์ธ์ด ํ์์ ๋ฐ๋ผ ๋ณ๊ฒฝ์ด ๋ ์๋
-
ํน์ ๋๋ฉ์ธ์ ๊ฐ๋ ์ ์ผ๋ก ํํํ ๊ฒ.
- UML์ด๋ ๊ฐ์ฒด ๊ด๊ณ๋๋ก ํํํ๋ ๊ฐ๋ ์ ์ผ๋ก๋ง ํํํ๋ฉด ๋๋ค. (์๋ ๊ทธ๋ฆผ)
- ์ฌ๋ก: ์ฃผ๋ฌธ ๊ณผ์
- ์ฃผ๋ฌธ์๋ ์ค์ -> ๋ฐฐ์ก์ง ์ ๋ ฅ -> ๊ฒฐ์ ์๋จ ์ ํ ํ ๊ณ์ฐ -> ์ฃผ๋ฌธ ์๋ฃ -> ์ถ๋ฐ์ํ์, ๋ฐฐ์ก์ง ๋ณ๊ฒฝ ๊ฐ๋ฅ
-
๊ฐ์ฒด ๋ฟ๋ง ์๋๋ผ ๋๋ฉ์ธ์ ๋ํ๋ผ ์ ์๋ค๋ฉด, ์ดํด์ ์ง์ ๊ณต์ ๋์๋๋ฉด ๋ค๋ฅธ ๋์ฒด์ ๋ ๋ชจ๋ ๊ฐ๋ฅํ๋ค.
- e.g. ์ํ ๋ค์ด์ด๊ทธ๋จ (์๋ ํ์ธ)
- ๊ฒฐ์ ์ฌ๋ถ์ ๋ฐ๋ผ ์ํ ์ค๋น
- ์ถ๊ณ ๊ฐ ์๋ฃ๋๋ฉด ์ทจ์ ๋ถ๊ฐ
- e.g. ์ํ ๋ค์ด์ด๊ทธ๋จ (์๋ ํ์ธ)
- ๊ฐ์ฅ ํํ Application ์ํคํ
์ฒ
- 4 ๊ณ์ธต: ํํ - ์์ฉ - ๋๋ฉ์ธ - ์ธํ๋ผ(DB) (Layer Architecture ๋งํฌ)
- ํํ - UI. ์ฌ์ฉ์/์ธ๋ถ์์คํ ์์ฒญ ์ฒ๋ฆฌํ๊ณ ๋ณด์ฌ์ค๋ค.
- ์์ฉ - ์ฌ์ฉ์ ์์ฒญ ๊ธฐ๋ฅ ์คํ. ์ ๋ฌด ๋ก์ง X. ๋๋ฉ์ธ ๊ณ์ธต ์กฐํฉํด์ ๊ธฐ๋ฅ ์คํ.
- ๋๋ฉ์ธ - ๋๋ฉ์ธ ๊ท์น ๊ตฌํ.
- ์ธํ๋ผ - DB๋ ๋ฉ์์ง ์ธ๋ถ ์์คํ ์ฐ๋ํ์ฌ ์ฒ๋ฆฌ
- ์์ธก: ์ฃผ๋ฌธ- ์ฃผ๋ฌธ, ๊ณ์ฐ, ๋ฐฐ์ก
- 4 ๊ณ์ธต: ํํ - ์์ฉ - ๋๋ฉ์ธ - ์ธํ๋ผ(DB) (Layer Architecture ๋งํฌ)
- ์ด๋ค ์ฝ๋๊ฐ ๋๋ฉ์ธ ๊ณ์ธต์?
- ์ฌ๋ก: ์ฃผ๋ฌธ ๋๋ฉ์ธ ๊ท์น
- ๊ท์น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, ์ฌ์ฉ์ ์คํ ๋ฆฌ๋ก ์ด์ ๋ง๋ค๊ธฐ.
-
๋ชจ๋ธ๋ง ๊ธฐ๋ณธ ์์ ๋ค
- ํต์ฌ ๊ตฌ์ฑ ์์
- ๊ท์น
- ๊ธฐ๋ฅ
-
์ฃผ๋ฌธ ๊ด๋ จ ์๊ตฌ์ฌํญ
- ์ต์ ํ ์ข ๋ฅ ์ด์์ ์ํ์ ์ฃผ๋ฌธํด์ผ ํ๋ค .
- ํ ์ํ์ ํ๊ฐ ์ด์ ์ฃผ๋ฌธํ ์ ์๋ค.
- ์ด ์ฃผ๋ฌธ ๊ธ์ก์ ๊ฐ ์ํ์ ๊ตฌ๋งค ๊ฐ๊ฒฉํฉ์ ๋ชจ๋ ๋ํ ๊ธ์ก ์ด๋ค .
- ๊ฐ ์ํ์ ๊ตฌ๋งค ๊ฐ๊ฒฉ ํฉ์ ์ํ ๊ฐ๊ฒฉ์ ๊ตฌ๋งค ๊ฐ์๋ฅผ ๊ณฑํ ๊ฐ ์ด๋ค .
- ์ฃผ๋ฌธํ ๋ ๋ฐฐ์ก์ง ์ ๋ณด๋ฅผ ๋ฐ๋์ ์ง์ ํด์ผ ํ ๋ค .
- ๋ฐฐ์ก์ง ์ ๋ณด๋ ๋ฐ๋ ์ฌ๋ ์ด๋ฆ, ์ ํ๋ฒํธ, ์ฃผ์๋ก ๊ตฌ์ฑ ๋๋ค .
- ์ถ๊ณ ๋ฅผ ํ๋ฉด ๋ฐฐ์ก์ง ์ ๋ณด๋ฅผ ๋ณ๊ฒฝ ํ ์ ์๋ค .
- ์ถ๊ณ ์ ์ ์ฃผ๋ฌธ์ ์ทจ์ํ ์ ์๋ค .
- ๊ณ ๊ฐ์ด ๊ฒฐ์ ๋ฅผ ์๋ฃ ํ๊ธฐ ์ ์๋ ์ํ์ ์ค๋น ํ์ง ์๋๋ค .
-
์ด๋ก ์ ์ ์๋ 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
- ์๊ฐ์ด ํ๋ฅด๋ฉฐ ๋๋ฉ์ธ์ ๋ ์ ์์๊ธฐ์, ๋ฆฌํฉํ ๋ง
- ๋ฐฐ์ก์ง ์ ๋ณด ๋ณ๊ฒฝ์ ์ฃผ๋ฌธ์ทจ์๊ฐ "์ถ๊ณ ์ ์ ๊ฐ๋ฅ" ์ด๋ผ๋ ์ ์ฝ์กฐ๊ฑด์ ์ฐพ๊ฒ๋์๊ธฐ์~
- ์๊ฐ์ด ํ๋ฅด๋ฉฐ ๋๋ฉ์ธ์ ๋ ์ ์์๊ธฐ์, ๋ฆฌํฉํ ๋ง
- ์ฃผ๋ชฉ: 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");
}
}
- ๋์ถํ ๋ชจ๋ธ์ ์ํฐํฐ์ ๋ฐธ๋ฅ๋ก ๊ตฌ๋ถ ๊ฐ๋ฅ
- ๋๋ฉ์ธ ๊ตฌํ์ ์ํด, ๋์ ์ฐจ์ด๋ฅผ ๋ช ํํ๊ฒ ์ดํดํด์ผ ํ๋ค.
- ์ฌ๊ธฐ์ 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; } }
- ํน์ ๊ท์น์ ๋ฐ๋ผ ์์ฑ
- UUID
- ๊ฐ ์ง์ ์ ๋ ฅ
- ์ผ๋ จ๋ฒํธ ์ฌ์ฉ
-
ํน์ ๊ท์น์ ๋ฐ๋ผ ์์ฑ
- ์ฌ๋ก: ์๋ง์กด/์๋ผ๋ ์จ๋ผ์ธ์์
- ์ฃผ๋ฌธ๋ฒํธ, ์ด์ก์ฅ๋ฒํธ, ์นด๋๋ฒํธ๋ ํน์ ๊ท์น์ ๋ฐ๋ผ ์์ฑ
- ํํ ์ฌ์ฉ ๋ฐฉ๋ฒ
- ์๊ฐ๊ณผ ๋ค๋ฅธ ๊ฐ์ ํจ๊ป ์กฐํฉ
- 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; // -> ์๋ฏธ์ ์ผ๋ก ๋ ์ํํ๋๋ค.
- ๊ณ ์ณ์ผํ ๊ฒ: ์ต๊ด์ ์ถ๊ฐ = 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
- ์ด๋ ต์ง๋ง, ์๊ฐ ๋ค์ฌ ์ฐพ์ ์ ์ ํ ๋จ์ด๋ฅผ ๊ณ ๋ฅด์.