"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

오늘이군

DDD (Domain Driven Design) 본문

삶../프로그래밍

DDD (Domain Driven Design)

오늘이군 2017. 7. 4. 09:30
반응형

※ 추천 글

https://www.youtube.com/watch?v=N3NSISzolSw

https://www.youtube.com/user/egoing2/search?query=%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5

http://andoli.tistory.com/87

http://scroogy.tistory.com/40

 

※ 추천 책

DDD START! - 최범균 
도메인 주도 설계 - 에릭 에반스  (보고 있는데 너무 어렵네요ㅠ)

※ 확인 중

http://tbang.tistory.com/104

1. 객체화

public static void main(String[] args) {

int left, right;
// 연산1
left = 10;
right = 20;
sum(left, right);
avg(left, right);

// 연산2
left = 20;
right = 40;
sum(left, right);
avg(left, right);

}

연산1 연산2가 반복이 되고 있습니다. 객체지향이전의 프로그램에서는 다음과 같은 함수형태로 해결을 하게 될 것입니다.

public void cal(left, right) {
    sum(left, right);
    avg(left, right);
}

public static void main(String[] args) {
    int left, right;
    cal(10, 20);
    cal(20, 40);
}

하지만 객체지향 프로그램에서는 클래스를 추가하여 다음과 같이 처리 할 수 있습니다.

public class Calulator {
    int left, right;
    public void setOprands(int a, int b)
        this.left = a; this.right = b;
    public void sum() 
        print(this.left + this.right);
    public void avg() 
        print((this.left + this.right) / 2);
}

public static void main(String[] args) {
    
Calulator c1 = Calulator();
    c1.setOprands(10, 20);
    c1.sum();
    c1.avg();
    
Calulator c2 = Calulator();
    c2.setOprands(10, 20);
    c2.sum();
    c2.avg();
}

언뜻 보기엔 파일도 추가로 만들어야 하고, 더 복잡하고 불편하다 생각될 수 있습니다.

하지만 객체지향 이전의 프로그래밍에서는 sum 과 avg 가 항상 실행 됩니다. avg 만 실행되어야 하거나 sum 과 avg 외에 다른 기능이 추가되야 하는 요건이 추가된다면 별도의 함수로 만들어야 할 것입니다. 그렇다면, 또 중복에 대한 고민을 안 할 수 가 없게 됩니다.

하지만 두번째와 같이 객체화를 하게 되면 도메인(Calculator) 에 도메인 관련된 로직을 추가하고, 호출하는 쪽에서는 필요한 것만 사용을 하게 하면 된다. 이는 숫자의 연산과 관계 된 로직은 도메인에 집중되게 되어 응집도가 높아져 중복을 방지하게 될 수 있게 되는 것이다.

2. Common Service

일반적인 spring, java 기반 프로젝트에서는 비즈니스 로직은 service 계층에 담고, 공통적인 Service 는 common 으로 빼서 각 Service 에서 호출하게 작업을 했었습니다.

처음 개발할 때는 좋았으나 시간이 지나고 여러사람들의 손을 거치고 난 뒤 소스는
공통메서드2 공통메서드3 ... 이 추가가 되어 있고
업무 Service 에서는 공통에서 가져온 뒤 재가공을 하는 등 난잡해지는 경우가 대부분이었습니다.

이러한 것을 위에서 살펴본 객체화와 DDD 가 해결 할 수 있을거라는 생각을 하게 됩니다.
모든 비즈니스 로직은 도메인 계층으로 집중을 하고 
서비스 계층에서는 도메인 로직 (비즈니스 로직) 을 호출/조합을 하는 형태로 구성을 하는 것이다.

3. Domain 분할

도메인 모델이 복잡해지면 한 개의 엔티티나 밸류에만 집중 하게 될 우려가 있습니다.
이 때 비즈니스 로직을 적절한 수준에서 분할(클래스, 메서드 단위) 을 해야 하는데, 앞서 살펴본 SOLID 의 SRP 의 기준에 맞춰 나눕니다.
각 클래스, 메서드는 하나의 기능만 수행하는 수준으로 나누는 것입니다. 

4. Aggregate root

각 클래스, 메서드는 하나의 기능만 수행하는 수준으로 나누면 기존과 다를 바가 없지 않느냐 라고 생각이 됩니다. (호출구조도 복잡해지고.. 이해하기도 어렵고... 등등)
이것을 애그리거트라는 개념으로 해소를 할 수가 있는데, 관련 객체를 묶어서 객체 군집 단위로 모델을 바라보는 것입니다. 

단순히 패키지 영역만 묶어놓고 마는 것이 아니라, 
군집에 속한 객체들을 관리하는 루트 엔티티(Aggregate root)를 지정하여 루트 엔티티가 아닌 객체는 캡슐화 하는 것입니다. (퍼사드 패턴)
Service 에서 객체를 호출 할 때는 애그리거트 루트를 통해서 간접적으로 애그리거트 내의 엔티티나 밸류 객체에 접근을 하는 것입니다.

※ tip

1. 불필요한 중복을 피하고 애그리거트 루트를 통해서만 도메인 로직을 구현하게 하려면 다음 두가지를 습관적으로 적용해야 합니다.

가. 단순히 필드를 변경하는 set 메서드를 public 으로 생성하지 않습니다. (cancle 이나 changePassword 처럼 꼭 필요하고 의미가 잘 드러나는 이름으로 구현 - setOldPwd, setNewPwd 같은 형태가 아닌 changePassword(oldPwd, newPwd) 같은 형태로 set)
나. 밸류 타입은 불변으로 구현합니다. (새로운 밸류 객체를 할당하도록 유도)

2. 트랜잭션의 범위는 작게 합니다. (잠그는 테이블이 많아 질 수록 동시에 처리할 수 있는 트랜잭션의 갯수가 줄어들며, 이는 전체적인 성능<처리량> 을 떨어뜨립니다.)

가. 한 트랜잭션에서는 한 개의 애그리거트만 수정해야 합니다. (한 애그리거트가 다른 애그리거트의 기능에 의존하기 시작하면 애그리거트간 결합도가 높아집니다.)
나. 부득이하게 한 트랜잭션으로 두 개 이상의 애그리거트를 수정해야 하면 응용서비스에서 트랜잭션을 묶어 두 개 이상의 애그리거트를 수정하게 합니다.

3. 애그리거트 간 참조 시 주의사항

가. 유혹에 빠지지 않는다 : order.getMember().changeAddress(newShippingInfo.getAddress()); 와 같이 다른 애그리거트를 변경 시키는 코드는 의존 결합도를 높이게 됩니다.
나. 성능에 관련 된 여러가지 고민을 해야 합니다. 
다. 도메인마다 다른 DB를 사용하게 되는 등의 확장이 일어나는 경우에 대한 고민이 필요합니다. 

▷ 이러한 고민을 해결 하기 위해서는  id 를 이용한 간접 참조를 하면 합니다.
Customer customer = customerRepository.findById(order.getOrderer.getCustomerId());

 

 

반응형

'삶.. > 프로그래밍' 카테고리의 다른 글

알고리즘 - 시간 복잡도, 공간 복잡도, 빅오(O)표기법  (0) 2018.08.13
TDD (Test Driven Development)  (0) 2017.07.06
Sequence Diagram  (0) 2017.07.04
Class Diagram  (0) 2017.06.20
Business Process Model and Notation (BPMN)  (0) 2017.06.08

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
Comments