[DDD Start] - 아키텍처 개요

기석·2022년 6월 7일
0
post-thumbnail
  • 아키텍처
  • DIP
  • 도메인 영역의 주요 구성요소
  • 인프라스트럭처
  • 모듈

아키텍처

표현, 응용, 도메인, 인프라스트럭처는 아키텍처를 설계할 때 출현하는 전형적인 네 가지 영역이다.

  • 표현 영역

    • 사용자의 요청을 응용 영역에 전달, 응용 영역의 처리 결과를 다시 사용자에 보여줌
    • http 요청을 응용 영역이 필요로 하는 형식으로 전달.
    • 응용 영역의 응답을 http 응답으로 변환하여 전송.
    • ex) MVC 프레임워크
  • 응용 영역

    • 사용자에게 제공해야 할 기능을 구현
      • ex) 주문 등록, 주문 취소, 상품 상세 조회 (OrderService)
    • 로직을 직접 수행하기 보다는 도메인 모델에 로직 수행을 위임한다.
      • Order order ... order.cancel();
  • 도메인 영역

    • 도메인 모델을 구현한다.
    • 도메인의 핵심 로직을 구현한다.
      • ex) 주문 도메인은 배송지 변경, 결제 완료, 주문 총액 계산과 같은 핵심 로직 구현
  • 인프라 스트럭처

    • 구현 기술에 대한 것을 다룬다.
    • RDBMS 연동, 메시징 큐, 데이터 연동 처리.
    • 논리적인 개념을 표현하기보다 실제 구현을 다룬다.
    • 도메인, 응용, 표현 영역은 구현 기술을 사용한 코드를 직접 만들지 않고 인프라스트럭처 영역에서 제공하는 기능을 사용한다.

계층 구조 아키텍처

표현 -> 응용 -> 도메인 -> 인프라스트럭처

계층 구조는 그 특성상 상위 계층에서 하위 계층으로의 의존만 존재하고, 하위 계층은 상위 계층에 의존하지 않는다.

응용 영역과 도메인 영역은 DB나 외부 시스템 연동을 위해 인프라스트락처의 기능을 사용해야 한다. 그래서 이런 계층 구조를 사용하는 것은 직관적으로 이해하기 쉽다.

하지만 표현, 응용, 도메인 계층이 보다 상세한 구현 기술을 다루는 인프라 스트럭처 계층에 종속되는 것은 문제가 생길 수 있는 일이다.

할인 정책을 결정하는 RateDiscountPolicy를 이용한 OrderService 클래스를 작성하는 상황이다.

public class OrderService{
	private RateDiscountPolicy rateDiscountPolicy;
    
    public OrderService(){
    	this.rateDiscountPolicy = new RateDiscountPolicy;
    }
    
    public Money calculateDiscount(...){
    	return rateDiscountPolicy.calculate(price, member);
    }
}

OrderService가 잘 작동한다고 할 때, 위 코드의 문제는 무엇일까?
1. 테스트하기 어렵다.
2. 구현을 변경하기 어렵다.

억까로 느껴진다면 먼저 DIP를 적용한 코드를 보고, SOLID 원칙을 공부하길 추천한다.


DIP (Dependency Inversion Principle, 의존관계 역전 원칙)

프로그래머는 추상화에 의존해야하고, 구체화에 의존하면 안된다는 원칙이다.
좋은 객체 지향 설계를 위한 SOLID 원칙 중 하나다.

책에서는 고수준, 저수준 모듈이라는 말을 많이 사용한다.
나는 이 말이 딱히 와닿지 않아 고수준 => 추상화된, 저수준 => 구체화된으로 바꾸어 말하겠다.

앞선 코드에서 추상화된 모듈이 구체화된 모듈을 사용하면 구현 변경과 테스트가 어렵다는 문제점이 생긴다는 것을 알았다.

이 코드에 DIP 원칙을 적용해 추상화에 의존하도록 해서 문제를 해결해보자.
추상화에 의존하도록 하는 비밀은 인터페이스에 있다.

public interface DiscountPolicy{
	...
}
public class OrderService{
	private DiscountPolicy discountPolicy;
    
    public OrderService(DiscountPolicy discountPolicy){
    	this.discountPolicy = discountPolicy;
    }
    
    public Money calculateDiscount(...){
    	return discountPolicy.calculate(price, member);
    }
}

이렇게 하면 이제 OrderService에는 구체화된 클래스인 RateDiscountPolicy에 의존하는 코드가 없다.

실제 RateDiscountPolicy 객체는 외부에서 생성자를 통해서 전달받는다.

Discountpolicy discountPolicy = new RateDiscountPolicy();
OrderService orderService = new OrderService(discountPolicy);

이 것을 DI (Dependency Injection, 의존 관계 주입)이라고 한다.
이런 방법을 사용하면 구현 기술을 변경하더라도 OrderService를 변경할 필요가 없고,
RateDiscountPolicy를 완성하지 않아도 테스트를 할 수 있다.


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

  • 엔티티
  • 밸류
  • 애그리거트
    • 연관된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것.
    • 예) Order 엔티티, OrderLine 밸류, Orderer 밸류 -> 주문 애그리거트
  • 리포지터리
    • 도메인 모델의 영속성을 처리한다.
  • 도메인 서비스
    • 특정 엔티티에 속하지 않은 도메인 로직을 제공한다.
    • 예) 할인 금액 계산은 상품, 쿠폰, 회원 등 여러 엔티티와 밸류를 필요로 함

엔티티와 밸류

DB 테이블의 엔티티 vs 도메인 모델의 엔티티

가장 큰 차이점: 도메인 모델의 엔티티는 데이터와 함께 도메인 기능을 함께 제공
도메인 관점에서 기능을 구현하고 기능 구현을 캡슐화해서 데이터가 임의로 변경되는 것을 막는다.

애그리거트

도메인 모델이 복잡해지면 개발자가 전체 구조를 큰 틀에서 관리하기 힘들 수 있다.
도메인 모델에서 전체 구조를 이해하는 데 도움을 주는 것이 애그리거트다.

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

리포지터리

엔티티나 밸류가 요구사항에서 도출되는 도메인 모델이라면 리포지터리는 구현을 위한 도메인 모델이다.

profile
블로그 이사갔어요 https://kiseoky.tistory.com

0개의 댓글