태그 보관물: domain-driven-design

domain-driven-design

응용 프로그램 또는 도메인 서비스의 DDD 리포지토리 = domainService

요즘 DDD를 공부하고 있는데 DDD로 리포지토리를 관리하는 방법에 대한 몇 가지 질문이 있습니다.

실제로, 나는 두 가지 가능성을 만났다.

첫 번째

내가 읽은 서비스를 관리하는 첫 번째 방법은 응용 프로그램 서비스에 리포지토리와 도메인 모델을 주입하는 것입니다.

이러한 방식으로 응용 프로그램 서비스 방법 중 하나에서 도메인 서비스 방법 (비즈니스 규칙 확인)을 호출하고 조건이 양호하면 저장소를 데이터베이스에서 엔티티를 유지 / 검색하기 위해 특수한 방법으로 호출합니다.

이를 수행하는 간단한 방법은 다음과 같습니다.

class ApplicationService{

  constructor(domainService, repository){
    this.domainService = domainService
    this.repository = repository
  }

  postAction(data){
    if(this.domainService.validateRules(data)){
      this.repository.persist(new Entity(data.name, data.surname))
    }
    // ...
  }

}

두번째 것

두 번째 가능성은 대신 domainService 내부에 저장소를 삽입하고 도메인 서비스를 통해서만 저장소를 사용하는 것입니다.

class ApplicationService{

  constructor(domainService){
    this.domainService = domainService
  }

  postAction(data){
    if(this.domainService.persist(data)){
      console.log('all is good')
    }
    // ...
  }

}

class DomainService{

  constructor(repository){
    this.repository = repository
  }

  persist(data){
    if(this.validateRules(data)){
      this.repository.save(new Entity(data.name))
    }
  }

  validateRules(data){
    // returns a rule matching
  }

}

지금부터 나는 어느 것이 가장 좋은지 (하나가 가장 좋은 것이 있는지) 또는 그들이 맥락에서 두 가지를 의미하는 것을 구별 할 수 없습니다.

어느 쪽이 다른 쪽보다 낫고 왜 그럴 수 있는지 예를 들어 주시겠습니까?



답변

짧은 대답은 응용 프로그램 서비스 또는 도메인 서비스에서 리포지토리를 사용할 수 있다는 것입니다. 그러나 그 이유와 방법을 고려하는 것이 중요합니다.

도메인 서비스의 목적

도메인 서비스는 도메인 개념 / 논리를 캡슐화해야합니다. 도메인 서비스 방법은 다음과 같습니다.

domainService.persist(data)

유비쿼터스 언어persist 의 일부가 아니기 때문에 도메인 서비스에 속하지 않으며 지속성 조작은 도메인 비즈니스 로직의 일부가 아닙니다.

일반적으로 도메인 서비스는 둘 이상의 집계를 조정하거나 작업해야하는 비즈니스 규칙 / 논리가있는 경우 유용합니다. 논리에 하나의 집계 만 포함 된 경우 해당 집계의 엔티티에 대한 메소드에 있어야합니다.

응용 프로그램 서비스의 리포지토리

따라서 그러한 의미에서 귀하의 예에서는 첫 번째 옵션을 선호하지만 도메인 서비스가 API의 원시 데이터를 허용하므로 개선해야 할 여지가 있습니다. 왜 도메인 서비스가 data? 의 구조에 대해 알아야 합니까? 또한 데이터는 단일 집계에만 관련된 것으로 보이므로 도메인 서비스 사용에 대한 가치는 제한적입니다. 일반적으로 엔터티 생성자 안에 유효성 검사를 넣습니다. 예 :

postAction(data){

  Entity entity = new Entity(data.name, data.surname);

  this.repository.persist(entity);

  // ...
}

유효하지 않은 경우 예외를 던집니다. 애플리케이션 프레임 워크에 따라 예외를 포착하고 API 유형에 대한 적절한 응답 (예 : REST API의 경우)에 대해 400 상태 코드를 리턴하는 일관된 메커니즘을 갖는 것이 간단 할 수 있습니다.

도메인 서비스의 리포지토리

위의 내용에도 불구하고 도메인 서비스에 리포지토리를 주입하여 사용하는 것이 유용하지만 리포지토리가 구현되어 집계 루트 만 허용하고 반환 할뿐 아니라 여러 집계를 포함하는 논리를 추상화하는 경우에만 유용합니다. 예 :

postAction(data){

  this.domainService.doSomeBusinessProcess(data.name, data.surname, data.otherAggregateId);

  // ...
}

도메인 서비스의 구현은 다음과 같습니다.

doSomeBusinessProcess(name, surname, otherAggregateId) {

  OtherEntity otherEntity = this.otherEntityRepository.get(otherAggregateId);

  Entity entity = this.entityFactory.create(name, surname);

  int calculationResult = this.someCalculationMethod(entity, otherEntity);

  entity.applyCalculationResultWithBusinessMeaningfulName(calculationResult);

  this.entityRepository.add(entity);

}

결론

여기서 핵심은 도메인 서비스 가 유비쿼터스 언어의 일부인 프로세스를 캡슐화 한다는 것입니다. 역할을 수행하려면 리포지토리를 사용해야하며 그렇게하는 것이 좋습니다.

그러나 리포지토리를 래핑하는 도메인 서비스를 persist추가 하는 방법은 가치가 거의 없습니다.

이를 바탕으로 응용 프로그램 서비스가 단일 집계 작업 만 요구하는 사용 사례를 표현하는 경우 응용 프로그램 서비스에서 직접 리포지토리를 사용하는 데 아무런 문제가 없습니다.


답변

허용되는 답변에 문제가 있습니다.

도메인 모델은 저장소에 의존 할 수 없으며 도메인 서비스는 도메인 모델의 일부입니다-> 도메인 서비스는 저장소에 의존해서는 안됩니다.

대신 응용 프로그램 서비스에서 이미 비즈니스 논리 실행에 필요한 모든 엔터티를 조립 한 다음 모델에 인스턴스화 된 개체를 제공하면됩니다.

귀하의 예에 따르면 다음과 같이 보일 수 있습니다.

class ApplicationService{

  constructor(domainService, repository){
    this.domainService = domainService
    this.repositoryA = repositoryA
    this.repositoryB = repositoryB
    this.repositoryC = repositoryC
  }

  // any parsing and/or pre-business validation already happened in controller or whoever is a caller
  executeUserStory(data){
    const entityA = this.repositoryA.get(data.criterionForEntityA)
    const entityB = this.repositoryB.get(data.criterionForEntityB)

    if(this.domainService.validateSomeBusinessRules(entityA, entityB)){
      this.repositoryC.persist(new EntityC(entityA.name, entityB.surname))
    }
    // ...
  }
}

따라서 경험 법칙 : 도메인 모델은 외부 레이어에 의존하지 않습니다

도메인 서비스 대 응용 프로그램
에서 이 문서 :

  • 도메인 서비스는 응용 프로그램 서비스가 API를 제공 할 목적으로 사용되는 경우 매우 세분화됩니다.

  • 도메인 서비스에는 엔터티 또는 값 개체에 자연적으로 배치 할 수없는 도메인 논리가 포함되어 있지만 응용 프로그램 서비스는 도메인 논리의 실행을 조정하고 자체적으로 도메인 논리를 구현하지는 않습니다.

  • 도메인 서비스 메소드는 다른 도메인 요소를 피연산자 및 리턴 값으로 가질 수 있지만 애플리케이션 서비스는 ID 값 및 기본 데이터 구조와 같은 사소한 피연산자에서 작동합니다.

  • 응용 프로그램 서비스는 도메인 논리를 실행하는 데 필요한 인프라 서비스에 대한 종속성을 선언합니다.


답변

서비스와 객체가 일관된 책임 세트를 캡슐화하지 않는 한 패턴 중 어느 것도 좋지 않습니다.

먼저 도메인 객체가 무엇인지 말하고 도메인 언어 내에서 무엇을 할 수 있는지에 대해 이야기하십시오. 유효하거나 유효하지 않은 경우 도메인 개체 자체의 속성으로 사용하지 않는 이유는 무엇입니까?

예를 들어 객체 유효성이 다른 객체의 관점에서만 의미가있는 경우 서비스 집합에 캡슐화 될 수있는 ‘도메인 객체에 대한 유효성 검사 규칙 X’가 있습니다.

객체를 검증하려면 비즈니스 규칙 내에 객체를 저장해야합니까? 아마 아닙니다. ‘객체 저장’책임은 일반적으로 별도의 저장소 객체에 있습니다.

이제 다양한 책임을 다루고, 객체를 생성하고, 유효성을 검사하고, 유효한 경우 저장하려는 작업을 수행했습니다.

이 작업이 도메인 개체에 고유합니까? 그런 다음 해당 객체의 일부로 만드십시오.ExamQuestion.Answer(string answer)

도메인의 다른 부분에 적합합니까? 거기에 넣어Basket.Purchase(Order order)

ADM REST 서비스를 원하십니까? 자 그리고 나서.

Controller.Post(json)
{
    parse(json);
    verify(parsedStruct);
    save(parsedStruct);
    return 400;
}


답변