728x90
도메인 주도 설계(Domain-Driven Design, DDD)란?
- 에릭 에반스가 제안한 소프트웨어 설계 방법론
- 비즈니스 도메인을 중심으로 소프트웨어를 설계하고 개발하는 방식
- 쉽게 말해 비즈니스의 핵심 개념과 로직을 코드에 잘 녹여내는 것이 DDD의 핵심
DDD의 핵심 개념
도메인(Domain)이란?
- 소프트웨어가 해결하려는 특정 비즈니스 영역
- 예를 들어 쇼핑몰이라면 상품, 주문, 결제 등이 도메인이 될 수 있음
바운디드 컨텍스트 (Bounded Context)
- 하나의 도메인은 여러 개의 서브 도메인으로 나뉠 수 있음
- 하지만 각 도메인은 명확한 경계(Context)를 가져야 함
- 예를 들어, 주문(Order)이라는 개념이 배송팀과 고객센터에서 다르게 해석될 수도 있기 때문에 각 도메인의 경계를 명확히 나누는 것이 중요
- 배송팀의 주문 → 배송 상태를 추적하는 정보 중심
- 고객센터의 주문 → 환불 및 고객 응대 정보 중심
- 따라서 두 팀이 공유하는 주문이라는 개념이라도, 서로 다르게 바라볼 수 있고 이를 바운디드 컨텍스트를 통해 분리하는 것이 중요함
엔티티 (Entity) vs 밸류 객체 (Value Object)
- 엔티티(Entity)
- 고유한 ID를 가지며, 상태가 변할 수 있는 객체
- 예 : User, Order, Product
class Order {
private Long id; // 고유한 식별자
private List<OrderItem> items;
private String status;
}
- 밸류 객체 (Value Object, VO)
- ID 없이 값 자체로 의미를 가지는 객체
- 불변(Immutable) 값이며 변경이 아닌 새로운 값으로 교체하는 방식 사용
- 예: Money, Address, DiscountPolicy
class Money {
private final BigDecimal amount;
private final String currency;
}
애그리게이트 (Aggregate)
- 하나 이상의 엔티티(Entity)와 VO(Value Object)로 이루어진 트랜잭션 단위 그룹
- 애그리게이트 루트(Aggregate Root)에서 데이터 변경을 관리하며, 외부에서 직접 내부 엔티티를 변경 불가
- 예 : 주문과 주문 아이템
class Order { // Aggregate Root
private Long id;
private List<OrderItem> items;
private String status;
public void addItem(OrderItem item) {
items.add(item);
}
}
class OrderItem { // 내부 엔티티 (외부에서 직접 접근 X)
private Long productId;
private int quantity;
}
- Order가 애그리게이트 루트이므로, OrderItem을 변경하려면 반드시 Order를 통해야 함
애그리게이트 루트(Aggregate Root)
- 애그리게이트 안에는 1개 이상의 엔티티 또는 밸류 객체가 존재
- 하나의 애그리게이트를 대표하는 엔티티를 애그리게이트 루트라고 함
도메인 | 애그리게이트 | 애그리게이트 루트 | 포함된 엔티티 및 밸류 객체 |
회원 | 회원 | 회원 정보 | 회원 포인트 |
주문 | 주문 | 주문 정보 | 배달 주문자, 배달 정보, 배달 추적 정보, 배달 주소 정보 |
음식 | 음식 | 음식 정보 | X |
결제 | 결제 | 결제 정보 | X |
- 회원 도메인에서는 회원 정보가 애그리게이트 루트이며 회원 포인트는 그 내부의 엔티티 혹은 밸류 객체일 수 있음
- 주문 도메인에서는 주문 정보가 애그리게이트 루트이며 배달 정보, 배달 주소 같은 것은 부차적인 엔티티 또는 VO로 포함
- 애그리게이트 루트는 직접 다른 애그리게이트의 내부 엔티티를 참조하면 안 됨
- 예를 들어, 회원 애그리게이트가 주문 애그리게이트 내부의 배달 정보를 직접 참조하면 안 됨
- 대신 주문 정보의 ID를 저장해서 참조하는 방식(참조 ID 또는 도메인 이벤트 사용)이 바람직함
- 애그리게이트 내부의 변경은 반드시 애그리게이트 루트를 통해야 함
- 예를 들어, 회원 포인트를 변경하려면 회원 정보(애그리게이트 루트)를 통해 변경
- 주문 정보를 통해서만 배달 정보를 변경
도메인 서비스 (Domain Service)
- 하나의 엔티티 또는 애그리게이트에서 처리하기 어려운 복잡한 도메인 로직을 담당하는 서비스
- 주로 여러 애그리게이트를 조합해야 하는 경우에 사용
- 예 : 할인 정책 적용
class DiscountService {
public Money applyDiscount(Order order, Coupon coupon) {
// 할인 로직
}
}
리포지토리 (Repository)
- 애그리게이트 단위로 데이터를 저장하고 조회하는 역할
- JPA와 함께 사용되며 CrudRepository 또는 JpaRepository를 활용 가능
interface OrderRepository extends JpaRepository<Order, Long> {
Optional<Order> findById(Long id);
}
애플리케이션 서비스 vs 도메인 서비스
- 애플리케이션 서비스 (Application Service)
- 트랜잭션 관리, 도메인 서비스 호출 등의 역할
- @Service 로 구현되는 일반적인 서비스 계층
@Service
class OrderService {
private final OrderRepository orderRepository;
private final DiscountService discountService;
public void placeOrder(Long userId, List<Long> productIds) {
// 주문 생성
Order order = new Order(userId, productIds);
// 할인 적용
Money discount = discountService.applyDiscount(order, coupon);
orderRepository.save(order);
}
}
- 도메인 서비스 (Domain Service)
- 애그리게이트 단위에서 처리하기 어려운 도메인 로직을 구현하는 서비스
class DiscountService {
public Money applyDiscount(Order order, Coupon coupon) {
// 할인 정책에 따른 가격 계산
}
}
DDD를 사용해야 하는 경우
- MSA 아키텍처에서 서비스 간 경계를 명확히 나누고 싶을 때
- 비즈니스 로직이 복잡하여 객체지향적인 설계가 필요할 때
- 장기적으로 유지보수와 확장이 중요한 대규모 프로젝트일 때
DD를 사용하지 않아도 되는 경우
- 비즈니스 로직이 단순하고 CRUD 위주일 때
- 빠른 개발이 중요하고 유지보수보다는 MVP 출시가 우선일 때
- DDD의 개념을 잘못 적용하면 오히려 코드가 복잡해질 수 있음
DDD와 MSA의 관계
- MSA에서는 서비스 간 경계를 명확하게 해야 하는데 DDD의 바운디드 컨텍스트 개념이 큰 도움을 줌
- 서비스 간 데이터 공유 시 애그리게이트 단위로 API를 설계하는 것이 중요
- 도메인 서비스, 애그리게이트, 이벤트 소싱 등의 개념이 MSA 설계와 잘 맞아떨어짐
728x90
'Computer Science' 카테고리의 다른 글
[Program Execution Process] 런 타임과 컴파일 타임 (0) | 2025.02.24 |
---|---|
[Software Development] 워터폴 방법론과 애자일 방법론 (0) | 2025.02.19 |
[Third Party] 서드 파티의 개념 (1) | 2024.12.16 |
[Cloud Service] 클라우드 서비스의 개념 (3) | 2024.12.15 |
[Software Engineering] CBD 방법론 (1) | 2024.12.03 |