코딩관계론

아키텍처 개요 본문

개발/Domain Driven Design

아키텍처 개요

개발자_티모 2024. 1. 2. 13:20
반응형

아키텍처는 전형적으로  '표현', '응용', '도메인','인프라스터럭처'의 영역으로 구성된다. 각각의 역활 및 코드에 대해서 알아보겠다.

표현영역

표현 영역의 역활은 http 요청이 오면 해당 요청을 응용 영역이 원하는 형태로 가공한 후 응용 영역에 전달한다. 예를 들면 

음식 주문 요청이 들어오면 그것을 DTO로 변환한 후 응용 영역에 전달하고 응용에서 처리한 결과를 사용자에게 전달한다

package presentation

type OrderController struct {
    // 필요한 의존성 주입
    AppService application.OrderAppService
}

func (oc *OrderController) HandleOrderRequest(requestDTO OrderRequestDTO) {
    // HTTP 요청을 응용 서비스가 필요로 하는 형태로 가공
    order := ConvertDTOToOrder(requestDTO)
    
    // 응용 서비스 호출
    oc.AppService.ProcessOrder(order)
}

 

응용영역

응용 영역의 역활은 사용자에게 제공할 기능을 구현한다. 예를들면 주문 취소, 상품 상세 조회와 같은 기능을 담당하고, 응용 영역은 해당 기능을 수행하기 위해 '주문 도메인', 상품 도메인등의 모델을 사용해서 기능을 구현한다.

중요한 점은 응용 영역에서는 로직을 수행하기보다는 도메인 모델에 로직 수행을 위임한다. 

package application

type OrderAppService struct {
    // 필요한 의존성 주입
    OrderDomainService domain.OrderDomainService
}

func (oas *OrderAppService) ProcessOrder(order Order) {
    // 주문 도메인 서비스를 이용한 로직 수행
    oas.OrderDomainService.CancelOrder(order)
}

 

도메인 영역

도메인 영역은 도메인 모델을 구현한다. Order 도메인의 핵심 로직이 이 영역에서 구현되어야 한다. 예를 들면 배송지 변경, 결제 완료, 주문 총액 계산등이다.

package domain

type OrderDomainService struct {
    // 필요한 의존성 주입
    Repository infrastructure.OrderRepository
}

func (ods *OrderDomainService) CancelOrder(order Order) {
    // 주문 취소 로직 구현
    // ...
    // 리포지터리를 통해 변경사항을 저장
    ods.Repository.SaveOrder(order)
}

 

인프라스트럭처 영역 

인프라스트럭처 영역은 구현 기술에 대한 것을 다룬다 예를들면 RDBMS 연동을 처리하고, 메세징 큐에 메세지를 전송허가나 수신하는 것을 구현한다. 

 

중요한 점은 도메인 영역, 응용 영역, 표현 영역은 구현 기술을 사용한 코드를 직접 만들지 않는다. 대신 인프라스트럭처 영역에서 제공하는 기능을 사용해서 필요한 기능을 개발한다. 

package infrastructure

type OrderRepository struct {
    // 필요한 의존성 주입
    DatabaseDriver DatabaseDriver
}

func (or *OrderRepository) SaveOrder(order Order) {
    // RDBMS에 주문 정보를 저장하는 로직
    or.DatabaseDriver.Save(order)
}

 

위의 설명과 같이 구성된 아키턱쳐의 중요한 점은 상위 계층에서 하위 계층으로의 의존만 존재하고 하위 계층은 상위 계층을 의존하지 않는다.  하지만 이렇게 되면 상위 계층이 상세한 구현 기술을 다루는 인프라스트럭처 계층에 종속된다는 점이다. 

아키텍쳐 요약도

 

위의 설명과 같이 구성된 아키턱쳐의 중요한 점은 상위 계층에서 하위 계층으로의 의존만 존재하고 하위 계층은 상위 계층을 의존하지 않는다.  하지만 이렇게 되면 상위 계층이 상세한 구현 기술을 다루는 인프라스트럭처 계층에 종속된다는 점이다. 때문에 DIP라는 개념이 도입된다.

 

의존성 역전 원칙 DIP

만약 아래와 같이 OrderAppService에서 OrderRepository를 사용하지 말고 rdb의  라이브러리에 직접 접근해서 oder 도메인을 저장한다고 생각했을 때 두 가지 문제점이 발생한다.

 

1. 코드를 변경하기가 어렵다

> RDB가 아니라 Document db를 사용하게 된다면 코드를 수정 범위가 커지게 된다.

 

2. 테스트의 용이성

> RDB에 연결되어야 만 테스트를 진행할 수 있다. 

// 응용 영역 (application)에서 구현에 직접 의존
package application

type OrderAppService struct {
}

func (oas *OrderAppService) ProcessOrder(order Order) {
    // 주문 도메인 서비스를 이용한 로직 수행
    rdb.open("local")
    rdb.save(order)
}

 

 

이를 해결하기 위해서 인터페이스를 도입한다. 아래의 방식으로 코드를 작성하면 고수준의 영역은 저수준의 영역에서 어떤 디비를 사용하고 있는지 몰라도 된다. OrderRepository를 구현한 구현체만 변경하면 된다.

 

주의사항은 인터페이스를 추출할 때 저수준의 기능을 추출하는 것이 아닌, 고수준에서 필요한 기능을 인터페이스로 추출해야 한다.

// 도메인 영역 (domain)에서 인터페이스 정의
package domain

type OrderRepository interface {
    SaveOrder(order Order)
}
--------------------------------------------------------------------------
// 응용 영역 (application)에서 인터페이스를 활용한 코드
package application

type OrderAppService struct {
    OrderRepository domain.OrderRepository
}

func (oas *OrderAppService) ProcessOrder(order Order) {
    // 주문 도메인 서비스를 이용한 로직 수행
    oas.OrderRepository.SaveOrder(order)
}

 

 

도메인 영역의 주요 구성 요소

엔터티

엔터티는 도메인에서 중요한 정보나 데이터를 표현하는데 사용되며, 시스템이 다루는 핵심 개념을 나타냅니다. 주로 데이터의 상태와 동작을 포함하며, 엔터티 간의 관계를 통해 비즈니스 규칙을 모델링하는 데에 중요한 역할을 합니다.

 

Db관계형 모델과 엔터티의 차이는 엔터티가 데이터와 함께 도메인 기능을 함께 제공한다는 점이 다른 점이다.

도메인 모델의 엔터티는 단순히 데이터를 담고 있는 데이터 구조라기보다는 데이터와 함께 기능을 제공하는 객체이다. 도메인 관점에서 기능을 구현하고, 기능 구현을 캡슐화해서 데이터가 임의로 변경되는 것을 막는다. 

 

벨류

벨류는 엔터티의 속성이나 상태를 나타내는데 사용되는 원시 데이터 타입이며, 엔터티의 특정 특성을 나타냅니다. 주로 엔터티의 속성으로 사용되며, 엔터티의 특정 면에서 어떤 값을 가지고 있는지를 정의합니다.

 

 

애그리거트

도메인이 커질수록 개발할 도메인 모델도 커지면서 많은 엔터티와 밸류가 출현한다. 도메인 모델이 복잡해지면서 개발자가 전체 구조가 아닌 한 개  엔터티와 벨류에만 집중하는 현상이 발생하고 큰 틀에서 모델을 관리할 수 없는 상황에 빠질 수 있다. 이를 방지하기 위해 애그리거트가 필요하다.

 

애그리거트란  관련된 여러 엔터티와 벨류를 하나로 묶어서 군집단위로 모델을 관리하는 것을 의미한다.

애그리거트는 군집에 속한 객체를 관리하는 루트 엔터티를 갖는다.  루트 엔터티는 애그리거트에 속해 있는 엔터티와 밸류 객체를 이용해서 애그리거트가 구현해야 할 기능을 제공한다. 

 

리포지토리

리포지토리는 애그리거트 단위로 도메인 객체를 저장하고 조회하는 기능을 정의한다

 

레포지터리는 엔터티나 에그리거트를 영속적으로 저장하고 관리하는 역할을 합니다. 데이터베이스나 다른 형태의 영속 저장소와 상호 작용하여 도메인 영역의 객체를 읽고 쓰는 인터페이스를 제공합니다. 레포지터리는 도메인 영역에서 데이터베이스와의 결합을 최소화하고 도메인 로직을 순수하게 유지하는 데 도움을 줍니다.

 

도메인서비스

도메인 서비스는 특정 엔터티나 에그리거트에 속하지 않는 도메인 로직을 수행하는 서비스입니다. 이 서비스는 특정 개체의 상태나 속성이 아닌 비즈니스 규칙이나 행위를 다루며, 여러 엔터티 또는 에그리거트 간의 상호 작용을 조정하는 역할을 합니다. 주로 도메인 영역에서 발생하는 복잡한 비즈니스 로직을 분리하고 모듈화하여 유지보수성을 향상시키고 응용프로그램의 확장성을 극대화하는데 사용됩니다. 도메인 서비스는 특정한 개념을 나타내는 것이 아니라, 비즈니스 프로세스나 규칙을 추상화하여 제공하는 특징이 있습니다.

 

요청흐름처리

요청 흐름 처리는 소프트웨어의 실행 요청이 처음 발생하는 지점인 표현 영역(컨트롤러)부터 시작됨. 표현 영역은 사용자의 HTTP 요청을 받아서 해당 요청을 응용 영역으로 전달하고. 응용 영역은 도메인 모델을 이용하여 실제 기능을 구현하며, 필요한 도메인 객체를 리포지토리에서 가져와 실행하거나 새로운 도메인 객체를 생성하여 리포지토리에 저장할 수 있다. 이를 통해 표현 영역과 응용 영역 간의 역할을 분리하고 도메인 모델을 적절히 활용한다.

반응형

'개발 > Domain Driven Design' 카테고리의 다른 글

애그리거트  (0) 2024.02.04