it-swarm.com.de

DDD-Repositorys im Anwendungs- oder Domänendienst

Ich studiere derzeit DDD und habe einige Fragen zur Verwaltung von Repositorys mit DDD.

Eigentlich habe ich zwei Möglichkeiten getroffen:

Erster

Die erste Möglichkeit, von mir gelesene Dienste zu verwalten, besteht darin, ein Repository und ein Domänenmodell in einen Anwendungsdienst einzufügen.

Auf diese Weise rufen wir in einer der Anwendungsdienstmethoden eine Domänendienstmethode auf (Überprüfung der Geschäftsregeln). Wenn die Bedingung erfüllt ist, wird das Repository über eine spezielle Methode aufgerufen, um die Entität zu speichern/aus der Datenbank abzurufen.

Ein einfacher Weg, dies zu tun, könnte sein:

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))
    }
    // ...
  }

}

Zweiter

Die zweite Möglichkeit besteht darin, das Repository stattdessen in den domainService einzuschleusen und das Repository nur über den Domänendienst zu verwenden:

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
  }

}

Von jetzt an kann ich nicht unterscheiden, welches das beste ist (wenn es eines am besten gibt) oder was sie beide in ihrem Kontext implizieren.

Können Sie mir ein Beispiel geben, wo einer besser sein könnte als der andere und warum?

31
mfrachet

Die kurze Antwort lautet: Sie können Repositorys von einem Anwendungsdienst oder einem Domänendienst verwenden. Es ist jedoch wichtig zu überlegen, warum und wie Sie dies tun.

Zweck eines Domain Service

Domänendienste sollten Domänenkonzepte/-logik kapseln - als solche die Domänendienstmethode:

domainService.persist(data)

gehört nicht zu einem Domänendienst, da persist nicht Teil der allgegenwärtigen Sprache ist und die Persistenzoperation nicht Teil der Domain-Geschäftslogik ist.

Im Allgemeinen sind Domänendienste nützlich, wenn Sie Geschäftsregeln/-logik haben, die die Koordination oder Arbeit mit mehr als einem Aggregat erfordern. Wenn die Logik nur ein Aggregat umfasst, sollte sie sich in einer Methode für die Entitäten dieses Aggregats befinden.

Repositorys in Application Services

In diesem Sinne bevorzuge ich in Ihrem Beispiel Ihre erste Option - aber selbst dort gibt es Raum für Verbesserungen, da Ihr Domänendienst Rohdaten von der API akzeptiert - warum sollte der Domänendienst über die Struktur von data?. Darüber hinaus scheinen sich die Daten nur auf ein einzelnes Aggregat zu beziehen, sodass die Verwendung eines Domänendienstes dafür nur einen begrenzten Wert hat. Im Allgemeinen würde ich die Validierung in den Entitätskonstruktor einfügen. z.B.

postAction(data){

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

  this.repository.persist(entity);

  // ...
}

und eine Ausnahme auslösen, wenn sie ungültig ist. Abhängig von Ihrem Anwendungsframework kann es einfach sein, einen konsistenten Mechanismus zum Abfangen der Ausnahme und zum Zuordnen zu der entsprechenden Antwort für den API-Typ zu haben - z. Geben Sie für eine REST API) einen 400-Statuscode zurück.

Repositorys in Domänendiensten

Ungeachtet des oben Gesagten ist es manchmal nützlich, ein Repository in einen Domänendienst einzufügen und zu verwenden, jedoch nur, wenn Ihre Repositorys so implementiert sind, dass sie nur Aggregatwurzeln akzeptieren und zurückgeben, und wenn Sie Logik abstrahieren, die mehrere Aggregate umfasst. z.B.

postAction(data){

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

  // ...
}

die Implementierung des Domänendienstes würde folgendermaßen aussehen:

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);

}

Fazit

Der Schlüssel hier ist, dass der Domänendienst kapselt ein Prozess ist, der Teil der allgegenwärtigen Sprache ist. Um seine Rolle zu erfüllen, müssen Repositorys verwendet werden - und das ist vollkommen in Ordnung.

Das Hinzufügen eines Domänendienstes, der ein Repository mit einer Methode namens persist umschließt, bringt jedoch wenig Wert.

Auf dieser Basis gibt es kein Problem, wenn Ihr Anwendungsdienst einen Anwendungsfall ausdrückt, bei dem nur mit einem einzelnen Aggregat gearbeitet werden muss, das Repository direkt vom Anwendungsdienst aus zu verwenden.

34
Chris Simon

Es gibt ein Problem mit der akzeptierten Antwort:

Das Domain-Modell darf nicht vom Repository abhängen und der Domain-Service ist Teil des Domain-Modells. -> Der Domain-Service sollte nicht vom Repository abhängen.

Stattdessen sollten Sie alle Entitäten zusammenstellen, die für die Ausführung der Geschäftslogik bereits im Anwendungsdienst erforderlich sind, und dann nur Ihre Modelle mit instanziierten Objekten versehen.

Anhand Ihres Beispiels könnte es so aussehen:

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))
    }
    // ...
  }
}

Faustregel: Das Domänenmodell hängt nicht von den äußeren Schichten ab

Anwendung gegen Domänendienst Von dieser Artikel :

  • Domänendienste sind sehr detailliert, da Anwendungsdienste eine Fassade sind, die eine API bereitstellen soll.

  • Domänendienste enthalten Domänenlogik, die natürlich nicht in einer Entität oder einem Wertobjekt platziert werden kann, während Anwendungsdienste die Ausführung der Domänenlogik koordinieren und selbst keine Domänenlogik implementieren.

  • Domänendienstdienstmethoden können andere Domänenelemente als Operanden und Rückgabewerte enthalten, während Anwendungsdienste mit trivialen Operanden wie Identitätswerten und primitiven Datenstrukturen arbeiten.

  • Anwendungsdienste deklarieren Abhängigkeiten von Infrastrukturdiensten, die zum Ausführen der Domänenlogik erforderlich sind.

4
sMs

Keines Ihrer Muster ist gut, es sei denn, Ihre Dienste und Objekte enthalten eine kohärente Verantwortung.

Sagen Sie zunächst, was Ihr Domain-Objekt ist, und sprechen Sie darüber, was es in der Domain-Sprache tun kann. Wenn es gültig oder ungültig sein kann, warum nicht als Eigenschaft des Domänenobjekts selbst?

Wenn zum Beispiel die Objektgültigkeit nur in Bezug auf ein anderes Objekt sinnvoll ist, haben Sie möglicherweise die Verantwortung 'Validierungsregel X für Domänenobjekte', die in einer Reihe von Diensten gekapselt werden kann.

Erfordert die Validierung eines Objekts das Speichern innerhalb Ihrer Geschäftsregeln? Wahrscheinlich nicht. Die Verantwortung für das Speichern von Objekten liegt normalerweise in einem separaten Repository-Objekt.

Jetzt haben Sie eine Operation, die Sie ausführen möchten, die eine Reihe von Verantwortlichkeiten abdeckt, ein Objekt erstellt, validiert und, falls gültig, gespeichert.

Ist diese Operation dem Domänenobjekt eigen? Machen Sie es dann zu einem Teil dieses Objekts, dh ExamQuestion.Answer(string answer)

Passt es zu einem anderen Teil Ihrer Domain? lege es dort hin Basket.Purchase(Order order)

Möchten Sie lieber ADM REST Services? Ok dann.

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