Repository DDD nell'applicazione o nel servizio di dominio

25

Sto studiando DDD in questi giorni e sto avendo alcune domande su come gestire i repository con DDD.

In realtà, ho incontrato due possibilità:

Primo

Il primo modo di gestire i servizi che ho letto è quello di iniettare un repository e un modello di dominio in un servizio applicativo.

In questo modo, in uno dei metodi del servizio applicativo, chiamiamo un metodo di servizio di dominio (controllando le regole aziendali) e se la condizione è buona, il repository viene chiamato su un metodo speciale per mantenere / recuperare l'entità dal database.

Un modo semplice per farlo potrebbe essere:

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

}

Secondo uno

La seconda possibilità è quella di iniettare il repository all'interno del domainService, invece, e utilizzare il repository solo attraverso il servizio di dominio:

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
  }

}

Da ora, non sono in grado di distinguere quale sia la migliore (se ce n'è una migliore) o cosa implicano entrambe nel loro contesto.

Puoi fornirmi un esempio in cui uno potrebbe essere migliore dell'altro e perché?

    
posta mfrachet 07.09.2016 - 19:44
fonte

2 risposte

25

La risposta breve è - puoi usare repository da un servizio applicativo, o un servizio di dominio - ma è importante considerare perché, e come, lo stai facendo.

Scopo di un servizio di dominio

I servizi di dominio dovrebbero incapsulare concetti / logica di dominio - come tale, il metodo del servizio di dominio:

domainService.persist(data)

non appartiene a un servizio di dominio, in quanto persist non fa parte del linguaggio ubiquitario e l'operazione di persistenza non fa parte della logica aziendale del dominio.

In generale, i servizi di dominio sono utili quando si hanno regole / logiche aziendali che richiedono il coordinamento o il lavoro con più di un aggregato. Se la logica coinvolge solo un aggregato, dovrebbe essere in un metodo sulle entità di quell'aggregato.

Repository in Application Services

Quindi in questo senso, nel tuo esempio, preferisco la tua prima opzione - ma anche lì c'è spazio per miglioramenti, dato che il tuo servizio di dominio accetta i dati grezzi dall'api - perché il servizio di dominio dovrebbe conoscere la struttura di% codice%?. Inoltre, i dati sembrano essere correlati solo a un singolo aggregato, quindi c'è un valore limitato nell'utilizzo di un servizio di dominio per questo - generalmente inserisco la convalida all'interno del costruttore di entità. per es.

postAction(data){

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

  this.repository.persist(entity);

  // ...
}

e genera un'eccezione se non è valida. A seconda del framework dell'applicazione, potrebbe essere semplice disporre di un meccanismo coerente per rilevare l'eccezione e associarlo alla risposta appropriata per il tipo API - ad es. per un'API REST, restituire un codice di stato 400.

Archivi dei servizi di dominio

Nonostante quanto sopra, a volte è utile iniettare e usare un repository in un servizio di dominio, ma solo se i repository sono implementati in modo tale da accettare e restituire solo le root aggregate, e anche dove si sta astringando la logica che coinvolge più aggregati . per es.

postAction(data){

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

  // ...
}

l'implementazione del servizio di dominio sarà simile a:

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

}

Conclusione

La chiave qui è che il servizio di dominio incapsula un processo che fa parte del linguaggio ubiquitario. Per svolgere il suo ruolo, ha bisogno di usare repository - e va benissimo farlo.

Ma aggiungere un servizio di dominio che avvolge un repository con un metodo chiamato data aggiunge poco valore.

Su questa base, se il servizio dell'applicazione sta esprimendo un caso d'uso che richiede di lavorare solo con un singolo aggregato, non c'è alcun problema nell'utilizzare il repository direttamente dal servizio dell'applicazione.

    
risposta data 07.09.2016 - 21:06
fonte
1

Nessuno dei tuoi modelli è buono a meno che i tuoi servizi e oggetti non incapsulino un insieme coerente di responsabilità.

Prima di tutto, dì ciò che è il tuo oggetto di dominio e parla di ciò che può fare nella lingua del dominio. Se può essere valido o non valido, perché non avere questo come una proprietà dell'oggetto stesso dominio?

Se per esempio la validità degli oggetti ha senso solo in termini di un altro oggetto allora forse hai una "regola di convalida X per gli oggetti di dominio" che può essere incapsulata in un insieme di servizi.

La convalida di un oggetto richiede la memorizzazione all'interno delle regole aziendali? Probabilmente no. La responsabilità di "immagazzinare oggetti" normalmente va in un oggetto repository separato.

Ora hai un'operazione da eseguire che copre una serie di responsabilità, crea un oggetto, lo convalida e, se valido, memorizzalo.

Questa operazione è intrinseca all'oggetto dominio? Quindi rendilo parte di quell'oggetto cioè ExamQuestion.Answer(string answer)

È compatibile con qualche altra parte del tuo dominio? mettilo lì Basket.Purchase(Order order)

Preferiresti fare i servizi ADM REST? Va bene allora.

Controller.Post(json) 
{ 
    parse(json); 
    verify(parsedStruct); 
    save(parsedStruct); 
    return 400;
}
    
risposta data 07.09.2016 - 23:22
fonte

Leggi altre domande sui tag