IDDD 11장. 팩토리

애그리게잇을 생성하는 책임을 가지는 메소드나 객체를 말한다.

  • 애그리게잇 생성을 캡슐화

도메인 모델 내의 팩토리

복잡한 객체와 애그리게잇 인스턴스를 생성하는 책임을 별도의 객체로 이동시키자. 여기서의 책임은 도메인 모델과 관련이 있지 않지만, 여전히 도메인 설계를 구성하는 한 요소다. 모든 복잡한 조립 과정을 캡슐화하고, 클라이언트가 인스턴스화된 객체의 구체적 클래스를 참조할 필요가 없도록 인터페이스를 제공하자. 전체 애그리게잇을 하나의 조각(원자성)으로 생성하고 고정자(Invariant)를 지정하자 [Evans]

  • 팩토리 메소드 : 이 책은 주로 여기만 나옴
  • 팩토리 객체(클래스)
    • 애그리게잇 생성이 매우 복잡하고 다른 객체의 도움이 필요하면 클래스 분리를 하는 것이 좋은 것 같다.

가능한 팩토리 위치

  • 애그리게잇 루트
    • 정정 생성자를 통해서 도메인 의도가 나오면 더 좋다. static Task#forDraft()
    • 아니면 상위 루트에서 하위 엔터리 생성도 가능 Project#createTask()
  • 도메인 서비스 : 서비스 기반 팩토리
    • ProjectService#createProject()
  • 팩토리 : 이 책은 다루지 않음
    • ProjectFactory#create()

애그리게잇 루트상의 팩토리 메소드

바운디드 컨텍스트 애그리게잇 팩토리 메소드
식별자와 액세스 컨텍스트 Tenant offerRegisterationInvitation()
    provisionGroup()
    provisionRole()
    registerUser()
협업 컨텍스트 Calendar scheduleCalendarEntry()
  Forun startDiscussion()
  Discussion post()
애자일 PM 컨텍스트 Product planBacklogItem()
    scheduleRelease()
    scheduleSprint()

CalendarEntry 인스턴스 생성하기

@Entity
class Calendar extends AbstractAggregateRoot {
    CalendarEntry scheduleCalendarEntry(...) {
        CalendarEntry calendarEntry = new CalendarEntry(...);
        this.registerEvent(new CalendarEntryScheduled(...));
        return calendarEntry;
    }
}
  • 유비쿼터스 언어에 부합되는 도메인이 표현됨 : scheduleCalendarEntry
  • 보호절이 없다 : 어짜피 new CalendarEntry(...)가 책임짐
  • Setter를 사용하지 않는다. > 원자성 + 부수효과 줄어듬 + 불변식 강제가 쉬움
  • 이벤트 발행 : CalendarEntryScheduled
  • 인자의 갯수가 줄어든다.
    • scheduleCalendarEntry(...)에서는 11개가 요구되지만, new CalendarEntryScheduled(...)에서는 9개로 줄어든다.
    • 개인적으로는 이것도 인자를 캡슐화 시켜서 객체로 말아버리는 것이 좋은 것 같다. 9개의 인자라도 너무 많은 것 같음.

하지만

  • CalendarEntry를 생성하기 위해서는 꼭 Calendar 인스턴스가 요구됨
    • 이는 DB 조회라는 부하가 추가됨

Discussion 인스턴스 생성하기

@Entity
class Forum extends AbstractAggregateRoot {
    Discussion startDiscussion(
        DiscussionId discussionId,
        Author author,
        String subject) {

        if (this.isClosed()) {
            throw new IllegalStateException("Forum is closed");
        }
        Discussion discussion = new Discussion(
            this.tenant(),
            this.forumId(),
            discussionId,
            author,
            subject);
        registerEvent(new DiscussionStarted(...));
        return discussion;
    }
}
  • 포럼이 열린 경우에만 토론을 시작할 수 있다. : this.isClosed()
  • 인자 5개 중 3개만 있으면 된다. : 나머지 2개는 포럼에서 제공
  • 역시나 유비쿼터스 언어가 표현된다. : startDiscussion

서비스의 팩토리

package com.saasovation.collaboration.domain.model.collaborator;

interface CollaboratorSerice {
    Author authorFrom(Tenant tenant, String identity);

    Creator creatorFrom(Tenant tenant, String identity);

    Moderator moderatorFrom(Tenant tenant, String identity);

    Owner ownerFrom(Tenant tenant, String identity);

    Participant participantFrom(Tenant tenant, String identity);
}
// infrastructure.services !!!
package com.saasovation.collaboration.infrastructure.services;

class UserRoleToCollaborationService implements CollaboratorSerice {
    @Override
    public Author authorFrom(Tenant tenant, String identity) {
        return (Author) userInRoleAdapter.toCollaborator(
            tenant, identity, "Author", Author.class);
        )
    }
}
package com.saasovation.collaboration.domain.model.collaborator;

class Author extends Collaborator {
    ...
}
  • 기술적 구현이므로 인프라 계층의 모듈에 위치한다.
  • 어댑터에 의존한다.
    • UserInRoleAdapter는 외래 컨텍스트와 의사소통 책임만 갖는다.
    • CollaboratorTranslator는 바운디드 컨텍스트 내 도메인 객체로 변환 책임만 갖는다.
  • 협업에서는 identity 식별자와 액세스에서는 username

마무리

  • 유비쿼터스 언어로 애그리게잇을 생성
  • 애그리게잇의 팩토리 메서드 VS 서비스의 팩토리 메서드

MJ

MJ
Backend 개발자 사람입니다. 어플리케이션의 복잡성을 다루는 DDD에 관심이 많습니다. 어제보다 더 나은 개발자가 되려고 항상 노력합니다.

spring boot 2.4.x 에서 openfeign + hystrix 통합하기

spring-boot 2.4.x spring-cloud 2020.x 의존성 상황에서 feign.hystrix.enabled=true가 안됨`feign.circuitbreaker.enabled=true` 로 바꿔보지만 openfeign과 hystr...… Continue reading

IDDD 14장. 애플리케이션

Published on June 19, 2018