
ํฅ์ฌ๊ณ ๋ ์ํคํ
์ฒ์ Application Layer๋ฅผ ๊ตฌ์ฑํ ๋๋
์ ํต์ ์ธ Input/Output Port ๋ฐฉ์๊ณผ CQRS๋ฅผ ์ ์ฉํ ๋ฐฉ์์ ์ค๋ฌด์ ์ฐจ์ด์ ๊ณผ ์ ํ ๊ธฐ์ค์ ๊ณ ๋ คํด๋ณผ ์ ์๋ค.
๋จ์ Input/Output Port๋ ํ๋์ ๋น์ฆ๋์ค ๋ก์ง์์ ์ฝ๊ธฐ์ ์ฐ๊ธฐ๋ฅผ ๋ชจ๋ ์ฒ๋ฆฌํ๋ ์ ํต์ ๋ฐฉ์์ด๋ฉฐ
CQRS ์ ์ฉ์ Command(์ฐ๊ธฐ)์ Query(์ฝ๊ธฐ)๋ฅผ ๋ช
ํํ ๋ถ๋ฆฌํ์ฌ ๊ฐ๊ฐ ์ต์ ํํ ์ ์๋ ๊ตฌ์กฐ์ด๋ค.
์ค๋ฌด์์๋ ๋๋ฉ์ธ ๋ณต์ก๋์ ์ฑ๋ฅ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ์ ํํ๋ค.
๋จ์ CRUD ์ค์ฌ์ด๋ฉด Input/Output Port๋ก ์ถฉ๋ถํ๊ณ ,
๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ์กฐํ ์ต์ ํ๊ฐ ํ์ํ๋ฉด CQRS๋ฅผ ๊ณ ๋ คํด๋ณผ๋ง ํ๋ค.
์ค๋ฌด ์์
1. ๋จ์ Input/Output Port ๊ตฌ์กฐ
๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
application/
โโโ port/
โ โโโ in/
โ โ โโโ OrderUseCase.java
โ โโโ out/
โ โโโ LoadOrderPort.java
โ โโโ SaveOrderPort.java
โโโ service/
โโโ OrderService.java
์ฝ๋ ์์
// Input Port - ๋น์ฆ๋์ค ์ ์ค์ผ์ด์ค ์ ์
public interface OrderUseCase {
OrderResponse createOrder(CreateOrderCommand command);
OrderResponse getOrder(Long orderId);
OrderResponse updateOrderStatus(Long orderId, OrderStatus status);
}
// Service - ์ฝ๊ธฐ/์ฐ๊ธฐ ํผ์ฌ
@Service
@RequiredArgsConstructor
public class OrderService implements OrderUseCase {
private final LoadOrderPort loadOrderPort;
private final SaveOrderPort saveOrderPort;
@Transactional
public OrderResponse createOrder(CreateOrderCommand command) {
// ์ฐ๊ธฐ ๋ก์ง
Order order = Order.create(command);
saveOrderPort.save(order);
// ์ฝ๊ธฐ ๋ก์ง (์์ฑ ํ ์กฐํ)
return OrderResponse.from(order);
}
@Transactional(readOnly = true)
public OrderResponse getOrder(Long orderId) {
// ์ฝ๊ธฐ ๋ก์ง
Order order = loadOrderPort.loadById(orderId);
return OrderResponse.from(order);
}
}
2. CQRS ํจํด ์ ์ฉ ๊ตฌ์กฐ
๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
application/
โโโ port/
โ โโโ in/
โ โ โโโ command/
โ โ โ โโโ CreateOrderUseCase.java
โ โ โ โโโ UpdateOrderStatusUseCase.java
โ โ โโโ query/
โ โ โโโ GetOrderQuery.java
โ โ โโโ SearchOrdersQuery.java
โ โโโ out/
โ โโโ command/
โ โ โโโ OrderCommandPort.java
โ โโโ query/
โ โโโ OrderQueryPort.java
โโโ service/
โโโ command/
โ โโโ OrderCommandService.java
โโโ query/
โโโ OrderQueryService.java
์ฝ๋ ์์
// Command Port - ์ฐ๊ธฐ ์ ์ฉ
public interface CreateOrderUseCase {
OrderId execute(CreateOrderCommand command);
}
// Query Port - ์ฝ๊ธฐ ์ ์ฉ
public interface GetOrderQuery {
OrderDetailView execute(Long orderId);
}
// Command Service - ๋น์ฆ๋์ค ๋ก์ง ์ง์ค
@Service
@RequiredArgsConstructor
public class OrderCommandService implements CreateOrderUseCase {
private final OrderCommandPort commandPort;
@Transactional
public OrderId execute(CreateOrderCommand command) {
// ๋๋ฉ์ธ ๋ก์ง์๋ง ์ง์ค
Order order = Order.create(command);
commandPort.save(order);
return order.getId();
}
}
// Query Service - ์กฐํ ์ต์ ํ
@Service
@RequiredArgsConstructor
public class OrderQueryService implements GetOrderQuery {
private final OrderQueryPort queryPort; // QueryDSL, Native Query ๊ฐ๋ฅ
@Transactional(readOnly = true)
public OrderDetailView execute(Long orderId) {
// DTO ์ง์ ๋ฐํ์ผ๋ก ์ฑ๋ฅ ์ต์ ํ
return queryPort.findOrderDetailById(orderId);
}
}
Port vs CQRS ํจํด ์์ธ ๋น๊ต
์ ๋ถ๋ฆฌํ๋๊ฐ?
1. ์ฑ ์์ ๋ช ํ์ฑ
- Input/Output: UseCase๊ฐ ์ฝ๊ธฐ/์ฐ๊ธฐ ๋ชจ๋ ๋ด๋นํ์ฌ ์ฑ ์์ด ํผ์ฌ
- CQRS: Command๋ ์ํ ๋ณ๊ฒฝ๋ง, Query๋ ์กฐํ๋ง ๋ด๋นํ์ฌ ๋จ์ผ ์ฑ ์ ์์น ์ค์
2. ์ฑ๋ฅ ์ต์ ํ
// Input/Output: Entity ๊ธฐ๋ฐ ์กฐํ
Order order = loadOrderPort.loadById(orderId);
// → ๋ถํ์ํ ์ฐ๊ด๊ด๊ณ ๋ก๋ฉ ๊ฐ๋ฅ์ฑ
// CQRS: DTO ์ง์ ์กฐํ
OrderDetailView view = queryPort.findOrderDetailById(orderId);
// → ํ์ํ ์ปฌ๋ผ๋ง SELECT, JOIN ์ต์ ํ
3. ํ์ฅ์ฑ
- Input/Output: ์กฐํ ๋ก์ง ๋ณต์ก๋ ์ฆ๊ฐ ์ Service๊ฐ ๋น๋ํด์ง
- CQRS: Query Service ๋ ๋ฆฝ ํ์ฅ, ์ฝ๊ธฐ ์ ์ฉ DB ๋ถ๋ฆฌ ๊ฐ๋ฅ
์ค๋ฌด ์ ํ ๊ธฐ์ค
1. Input/Output Port ์ ํฉํ ๊ฒฝ์ฐ
- ๋จ์ CRUD ๋๋ฉ์ธ (์ฌ์ฉ์ ๊ด๋ฆฌ, ์ค์ ๊ด๋ฆฌ)
- ์กฐํ์ ์์ ์ด 1:1 ๋์
- ํ ๊ท๋ชจ๊ฐ ์๊ณ ๋น ๋ฅธ ๊ฐ๋ฐ ํ์
- ํธ๋ํฝ์ด ๋ฎ๊ณ ์ฑ๋ฅ ์๊ตฌ์ฌํญ ๋จ์
์์: ์ฌ๋ด ๊ด๋ฆฌ์ ๋๊ตฌ, ์ค์ ๊ด๋ฆฌ ์์คํ
2. CQRS ์ ํฉํ ๊ฒฝ์ฐ
- ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง (์ฃผ๋ฌธ, ๊ฒฐ์ , ์ ์ฐ)
- ์กฐํ ์๊ตฌ์ฌํญ์ด ๋ค์ํ๊ณ ๋ณต์ก (๋์๋ณด๋, ๋ฆฌํฌํธ)
- ์ฝ๊ธฐ/์ฐ๊ธฐ ๋น์จ ์ฐจ์ด ํผ (์ฝ๊ธฐ 90%, ์ฐ๊ธฐ 10%)
- ์ด๋ฒคํธ ์์ฑ๊ณผ ํจ๊ป ์ฌ์ฉ
์์: ์ด์ปค๋จธ์ค ์ฃผ๋ฌธ ์์คํ , ๊ด๊ณ ๋ฐ์ดํฐ ์์ง ํ๋ซํผ
์ฃผ์์ฌํญ
Input/Output Port
- Service ๋น๋ํ ๊ฒฝ๊ณ : ๋ฉ์๋ 10๊ฐ ์ด์ ์ ๋ถ๋ฆฌ ๊ณ ๋ ค
- ํธ๋์ญ์ ๋ฒ์ ํผ๋: readOnly ํธ๋์ญ์ ๋๋ฝ ์ฃผ์
- Output Port ๊ณผ๋ํ ๋ถ๋ฆฌ: LoadPort, SavePort๋ฅผ Repository๋ก ํตํฉ ๊ณ ๋ ค
CQRS
- ๊ณผ๋ํ ๋ถ๋ฆฌ: ๋จ์ ๋๋ฉ์ธ์ ์ ์ฉ ์ ์ค๋ฒ์์ง๋์ด๋ง
- ์ฝ๋ ์ค๋ณต: Command/Query ๊ฐ ์ ์ฌ ๋ก์ง ๋ฐ์ ๊ฐ๋ฅ
- ํ์ต ๊ณก์ : ํ์ ๋ชจ๋๊ฐ ํจํด ์ดํด ํ์
๊ฒฐ๋ก
ํจํด ์ ์ฉ์ ์ ๋ต์ ์์ผ๋ฉฐ, ์ค๋ฌด์์๋ ๋๋ฉ์ธ ๋ณต์ก๋์ ์ฑ๋ฅ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ์ ํํจ.
ํ๋ก์ ํธ ์ด๊ธฐ์๋ Input/Output Port๋ก ์์ํ๊ณ , ๋๋ฉ์ธ ๋ณต์ก๋๊ฐ ์ฆ๊ฐํ๊ฑฐ๋ ์กฐํ ์ฑ๋ฅ ์ด์ ๋ฐ์ ์ CQRS๋ก ์ ์ง์ ์ ํํ๋ ๊ฒ์ด ์์ ํจ!
'Backend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| DDD ์์น(Domain Driven Design)์ด๋? (0) | 2026.01.03 |
|---|---|
| ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ๋? (0) | 2026.01.03 |