반응형

MSA를 알기 위해서는 우선 DDD를 먼저 알아야 한다고 한다.

DDD는 Domain Driven Design으로 도메인이라는 영역?, 집합?을 기준으로 아키텍처를 설계하는 것을 말한다.

 

DDD의 핵심 요소로는 Entity, Value Object, Aggregate, Aggregate Root, Domain Event, Domain Service, Application Service가 있으며 하나씩 알아보도록 할 것이다.

 

  • Entity

고유한 식별자로 구분되는 도메인 객체를 말한다.

객체의 Equal을 구분하는 방법은 파라미터, 참조를 확인하는 게 아니라 고유한 ID가 같은지 확인한다.

내부의 값들은 변경이 가능하며, 이 Entity 자체에 로직을 추가하여 사용한다.

data class User(
    val id: UUID,
    var name: String,
    var email: String
) {
    fun updateEmail(email: String) {
        this.email = email
    }
}

 

이런 식으로 id를 활용하여 동일성을 판단하고, updateEmail과 같은 함수들을 포함한다.

  • Value Object(값 객체)

값 자체로 동등성을 비교하는, id가 없는 객체이다.

값들을 전체 비교하여 동일한 객체로 간주한다.

내부의 값은 변경할 수 없으며, 필요하다면 객체를 새로 생성하여 사용해야 한다.

 

data class Address(
    val city: String,
    val street: String,
    val postalCode: String
)

 

이렇게 Id가 존재하지 않으며, parameter들만 동일하다면 동일한 객체이다.

 

  • Aggregate

관련된 Entity와 값 객체를 묶어서 일관성을 유지하는 군집을 말한다.

하나의 트랜잭션 단위이며, Aggregate Root를 사용해서만 외부 접근이 가능하다.

  • Aggregate Root

Aggregate 내의 최상위 Entity이며, Aggregate Root를 통해서만 Aggregate에 접근이 가능하다.

data class Order(
    val id: String,
    val customerId: String,
    var items: MutableList<OrderItem>,
    var status: OrderStatus
) {
    fun addItem(item: OrderItem) {
        items.add(item)
    }

    fun completeOrder() {
        status = OrderStatus.COMPLETED
    }
}

data class OrderItem(
    val productId: String,
    var quantity: Int,
    val price: Double
)

 

여기서 Order가 Aggregate Root, OrderItem이 Aggregate이며, OrderItem에 직접 접근하지 않고, Order의 메서드를 통해서만 접근하는 것을 볼 수 있다.

 

  • Domain Event

도메인 내에서 비즈니스적으로 중요한 이벤트를 명시하는 객체이다.

도메인 모델에서 비즈니스 이벤트를 명확하게 표현한다.

이벤트 발행 후 비동기로 데이터를 처리가능하다.

data class OrderCompletedEvent(
    val orderId: String,
    val completedAt: LocalDateTime
)

 

  • Domain Service

비즈니스 규칙을 처리하는 서비스이다.

Entity나 Value Object로 표현하기 어려운 비즈니스 로직을 담당하며, stateless 서비스이다.

class DiscountService {
    fun calculateDiscount(order: Order): Double {
        return if (order.items.size >= 5) 0.1 else 0.0
    }
}

 

Order에 넣기 애매한 비즈니스 규칙이기에 도메인 서비스로 분리한다.

도메인 엔티티와 상호작용하는 것을 볼 수 있다.

 

  • Application Service

비즈니스 흐름을 관리하는 서비스

유즈케이스를 중심으로 작업의 흐름을 제어하며, 도메인 서비스와 도메인 엔티티등을 호출해 작업을 조합한다.

상위 계층에서 외부 API 호출을 담당하며, 트랜잭션과 이벤트 발행의 책임이 있다.

@Service
class OrderService(
    private val orderRepository: OrderRepository,
    private val discountService: DiscountService
) {
    @Transactional
    fun createOrder(customerId: String, items: List<OrderItem>): Order {
        val order = Order(
            id = UUID.randomUUID().toString(),
            customerId = customerId,
            items = items.toMutableList(),
            status = OrderStatus.PENDING
        )
        // 할인 적용 (도메인 서비스 사용)
        val discount = discountService.calculateDiscount(order)
        if (discount > 0) {
            println("할인 적용: $discount")
        }

        // 주문 저장
        return orderRepository.save(order)
    }
}

 

도메인 서비스와 애플리케이션 서비스가 좀 헷갈리지만, 두 서비스는 호출하는 layer가 다른 것을 볼 수 있다.

'MSA' 카테고리의 다른 글

MSA에 CQRS 패턴 적용하기  (0) 2025.03.01
MSA에 Outbox 패턴 적용하기  (0) 2025.02.28
MSA에 SAGA 패턴 적용하기  (0) 2025.02.24
DDD에서 Hexagonal Architecture로 변경하기  (0) 2025.02.20
Spring + Kafka에서 avro 사용하기  (1) 2025.02.15

+ Recent posts