Servizi di dominio rispetto a fabbriche e radici aggregate

6

Dopo aver a che fare con DDD da mesi, sono ancora confuso riguardo agli scopi generali dei servizi di dominio, delle fabbriche e delle radici aggregate in relazione l'uno con l'altro, ad es. dove si sovrappongono nella loro responsabilità.

Esempio: ho bisogno di 1) creare un'entità di dominio complessa in una saga (gestore di processi) che è seguita da 2) un determinato evento di dominio che deve essere gestito altrove mentre 3) l'entità è chiaramente una radice aggregata che contrassegna un contesto limitato ad alcune altre entità.

  1. La fabbrica è responsabile della creazione dell'entità / radice aggregata
  2. Il servizio PU CAN creare l'entità, poiché lancia anche l'evento di dominio
  3. Il servizio CAN può fungere da root aggregato (creare 'subentity' di 'entity' con ID 4)
  4. La radice aggregata può creare e gestire le "sottenticità"

Quando introduco il concetto di una radice aggregata e una fabbrica nel mio dominio, un servizio non sembra più necessario. Tuttavia, se non lo sono, il servizio può gestire tutto ciò che è necessario e con le conoscenze e le dipendenze che ha.

Esempio di codice basato sul linguaggio ubiquitario di un'officina automobilistica

public class Car : AggregateRoot {

    private readonly IWheelRepository _wheels;
    private readonly IMessageBus _messageBus;

    public void AddWheel(Wheel wheel) {
        _wheels.Add(wheel);
        _messageBus.Raise(new WheelAddedEvent());
    }

}

public static class CarFactory {

    public static Car CreateCar(string model, int amountofWheels);

}

.. o ...

public class Car {

    public ICollection<Wheel> Wheels { get; set; }

}

public interface ICarService {

    Car CreateCar(args);
    void DeleteCar(args);
    Car AddWheel(int carId, Wheel wheel);

}
    
posta Acrotygma 05.05.2014 - 09:44
fonte

2 risposte

6

Il DDD è in parte una reazione ai modelli di dominio anemici, dove le tue entità avrebbero solo uno stato, ma non un comportamento.

È vero che in un certo senso potresti mettere tutto il comportamento di Car in un servizio separato, ma perché vorresti farlo? Affinché funzioni, devi esporre tutti i tipi di stato in Car, che normalmente preferiresti mantenere privato (come Wheels).
Se esponi Ruote in questo modo, qualsiasi codice può fare ogni sorta di cose funky per quella raccolta, al di fuori di qualsiasi normale flusso aziendale. Tieni presente che il punto di un aggregato è di avere qualcosa che sia consistentemente transazionale tra le transazioni del flusso aziendale. L'esposizione di Ruote del genere compromette completamente la sicurezza.
Ad esempio: supponiamo che la tua attività commerciale supporti solo le auto a quattro ruote. Se si incapsula l'accesso a Ruote, è possibile applicarlo. Se non lo fai, è completamente possibile aggiungere cento ruote a una macchina, perché le ruote sarebbero esposte.

Un servizio viene generalmente utilizzato come coordinatore. Un command handler (assumendo qualcosa come CQRS) traduce un oggetto comando in parametri più strongmente tipizzati, riducendo l'ossessione primitiva. Quindi può chiamare un servizio con quei parametri più "dominio-ish". Il servizio recupera le entità dai repository e invoca i comportamenti su di essi. A seconda dell'architettura, può quindi raccogliere eventuali modifiche (eventi) e trasferirle su un bus o altro.
In scenari più avanzati, dovresti utilizzare un'unità di lavoro che tiene traccia di tutte le tue entità e può raccogliere le modifiche da tutte loro in un colpo solo.

Per quanto riguarda una fabbrica (a parte), nulla ti impedisce di aggiungere un metodo factory statico alla tua classe aggregata, invece di creare un factory separato.

    
risposta data 05.05.2014 - 10:15
fonte
5

After dealing with DDD for months now, I'm still confused about the general purposes of domain services, factories and aggregate roots in relation to each other, e.g. where they overlap in their responsibility.

La radice aggregata è responsabile di garantire che lo stato sia coerente con l'invarianza di business. Nel gergo CQRS, si modifica il modello inviando comandi a una radice aggregata.

Il servizio di dominio è un meccanismo di supporto delle query per gli aggregati. Ad esempio, un servizio di dominio potrebbe supportare un calcolo. Il gestore di comandi passa il servizio di dominio all'aggregato, l'aggregato (elaborando un comando) ha bisogno del risultato del calcolo, quindi passerà al servizio di dominio le parti del suo stato che sono necessarie come input per il calcolo. Il servizio di dominio esegue il calcolo e restituisce il risultato e l'aggregato decide in merito al risultato.

"Fabbrica" può significare un paio di cose. Se stai usando la fabbrica per creare un'entità che si trova all'interno del limite aggregato, allora è solo un dettaglio di implementazione - potresti usarlo se la costruzione dell'oggetto è complicata.

In alcune circostanze, viene utilizzato il termine "Deposito". Questo di solito implica che fa parte del livello di persistenza (non parte del modello di dominio) responsabile della creazione di radici aggregate. Approssimativamente, il gestore di comandi (che fa parte dell'applicazione) convalida un comando, quindi utilizza il repository per caricare la radice aggregata a cui è indirizzato il comando. Il gestore comandi invoca quindi il metodo specificato nella radice aggregata, passando i parametri dall'oggetto comando e eventualmente passando anche il servizio dominio come argomento.

Nei tuoi esempi, la domanda chiave da porre è dove vive la responsabilità di decidere se un comando debba essere eseguito. L'applicazione è responsabile per assicurarsi che il comando sia ben formato (tutti i dati di comando sono presenti, i dati sono stati tradotti in valori riconosciuti dal dominio senza generare errori di convalida e così via). Ma chi decide "No, non puoi aggiungere una ruota in questo momento - le regole aziendali non lo permettono!"

Nel mondo DDD, è indiscutibilmente la responsabilità della radice aggregata. Quindi qualsiasi logica per determinare che dovrebbe essere nella radice aggregata e ICarService va via.

(L'implementazione alternativa, dove l'aggregato espone il suo stato, e il servizio delle macchine controlla le regole del business e manipola lo stato dell'oggetto, è considerato come un modello anti - un esempio di un aggregato "anemico". "in un aggregato c'è un odore di codice." I getter "in un aggregato sono spesso un odore di codice - specialmente in CQRS, dove la responsabilità di supportare le query dovrebbe essere" da qualche altra parte "- nel modello letto.)

    
risposta data 12.01.2016 - 02:23
fonte