2013-01-10 14 views
7

Ho uno scenario reale che è un perfetto modello di modello di dominio. È un campo che ha più quadranti con stati diversi su ogni quadrante. Quindi la mia radice aggregata è il campo. Ora ho una domanda importante: Voglio avere un modello di dominio ignorante perseverante, che penso abbia un senso. quindi dove dovrei chiamare l'aggiornamento sui metodi del repository? non nel modello di dominio, giusto? Quindi, come dovrebbero essere aggiornate le entità figlio radice aggregate nel database quando non vi è alcun proxy di tracciamento delle modifiche di questi oggetti e il repository non deve essere chiamato nelle entità? O frainteso il modello del modello di dominio?Dove chiamare repository.update in DDD?

è la mia domanda chiara? :) grazie in anticipo migliore Laurin

risposta

7

Quindi dove dovrei chiamare l'aggiornamento sui metodi del repository?

In un'architettura stereotipo DDD repository è solitamente chiamato da un servizio applicazione. Un servizio di applicazione è una classe che funge da facade che incapsula il tuo dominio e implementa casi di utilizzo del dominio orchestrando oggetti di dominio, archivi e altri servizi.

Non ho dimestichezza con il tuo dominio, ma suppongo che c'è un caso d'uso che sposta un State da un Quadrant in una Field ad un altro. Come hai affermato, lo Field è l'AR. Così si avrebbe un FieldApplicationService riferimento a un FieldRepository:

public class FieldApplicationService 
{ 
    readonly FieldRepository fieldRepository;  

    public void ShiftFieldState(int fieldId, string quadrant, string state) 
    { 
      // retrieve the Field AR 
      var field = this.fieldRepository.Get(fieldId); 
      if (field == null) 
       throw new Exception(); 

      // invoke behavior on the Field AR. 
      field.ShiftState(quadrant, state); 

      // commit changes. 
      this.fieldRepository.Update(field); 
    } 
} 

Il servizio di applicazione è di per sé molto sottile. Non implementa alcuna logica di dominio; esso orchestra e imposta solo le basi per l'esecuzione della logica di dominio che include l'accesso al repository. Tutto il codice dipendente dal tuo dominio, come il livello di presentazione o un servizio, invocherà la funzionalità del dominio attraverso questo servizio applicativo.

Il repository può essere implementato in vari modi. Può essere con un ORM come NHibernate, nel qual caso il monitoraggio delle modifiche è integrato e l'approccio abituale è quello di impegnare tutte le modifiche anziché chiamare un aggiornamento esplicito. NHibernate fornisce anche uno Unit of Work che consente di eseguire le modifiche a più entità come se fossero una sola.

Nel tuo caso, come hai affermato, non è necessario tenere traccia delle modifiche, quindi è necessaria una chiamata esplicita all'aggiornamento ed è responsabilità dell'implementazione del repository gestirla. Se si utilizza SQL Server come database, il metodo Update nel repository può semplicemente inviare tutte le proprietà di un Field a una stored procedure che aggiornerà le tabelle secondo necessità.

+0

Domanda: Cosa succede se * non è chiaro quali aggregati sono stati modificati *? Per esempio.il servizio applicativo chiama 'bookLendingService.LendBookToClient (book, client)'. Salva il libro? Salva cliente? Come possiamo essere certi che il servizio applicativo * conosca * e che questo non cambi mai? – Timo

+0

Ho sentito parlare di una regola di mutazione di un solo aggregato per transazione, che potrebbe essere una risposta alla mia domanda di cui sopra. In tal caso, qual è la ragione di questa regola? E come si fa a gestire uno scenario che richiede modifiche a più aggregati? – Timo

3

La mia soluzione è la radice di aggregazione alzerà alcuni eventi ai gestori di eventi esterni. Questi gestori di eventi chiameranno il repository per aggiornare il database. Avrai anche bisogno di un ServiceBus per registrare e inviare eventi. Vedere il mio esempio:

public class Field: AggregateRoot 
{ 
    public UpdateField() 
    { 
     // do some business 
     // and trigger FieldUpdatedEvent with necessary parameters 
     .... 
     // you can update some quadrants 
     // and trigger QuadrantsUpdatedEvent with necessary parameters 
    } 
} 

public class FieldEventHandlers: EventHandler 
{ 
    void Handle (FieldUpdatedEvent e) 
    { 
     repository.Update(e.Field); 
    } 
} 

public class QuadrantEventHandlers: EventHandler 
{ 
    void Handle (QuadrantsUpdatedEvent e) 
    { 
     repository.Update(e.Quadrant); 
    } 
} 
+0

Grazie per la tua risposta utile :) – LaurinSt

4

La radice di aggregazione (AR) viene aggiornata in tutto il mondo. Usando un'architettura message driven, da qualche parte c'è un gestore di comandi, ma diciamo per scopi generali che è un servizio. Il servizio ottiene l'AR da un repository, chiama i metodi rilevanti e salva l'AR nel repository.

L'AR non conosce il repository, non è la sua preoccupazione. Il repository salva quindi tutte le modulazioni AR come unità di lavoro (che è tutto o niente). Come fa il Repo, beh, dipende da come hai deciso la tua strategia di persistenza.

Se si utilizza Event Sourcing, l'AR genera eventi e Repo utilizzerà tali eventi per mantenere lo stato AR. Se si segue un approccio più comune, tale AR dovrebbe avere dati di stato da qualche parte esposti come proprietà, forse. Si chiama lo schema del ricordo. Il repository mantiene i dati in un commit.

Bu una cosa è certa: non pensare mai ai dettagli di persistenza, quando si ha a che fare con un oggetto Dominio. Ciò significa non accoppiare il dominio a un ORM oa qualche db specifico.

+0

Grazie a tutti voi ragazzi! – LaurinSt

3

Il "codice dell'applicazione" deve chiamare il repository. Il modo in cui è ospitato il codice dell'applicazione è un problema di infrastruttura. Alcuni esempi di come il codice dell'applicazione potrebbe essere ospitato sono il servizio WCF, come un'applicazione Winforms/WPF o su un server Web.

L'implementazione del repository è responsabile del rilevamento delle modifiche alla radice aggregata e alle sue entità figlio nonché al loro salvataggio nel db.

Ecco un esempio:

Domain Progetto

public DomainObject : AggregateRootBase //Implements IAggregateRoot 
{ 
    public void DoSomething() { } 
} 

public IDomainObjectRepository : IRepository<DomainObject>, IEnumerable 
{ 
    DomainObject this[object id] { get; set; } 
    void Add(DomainObject do); 
    void Remove(DomainObject do); 
    int IndexOf(DomainObject do); 
    object IDof(DomainObject do); 
    IEnumerator<DomainObject> GetEnumerator(); 
} 

progetto di implementazione

public SqlDomainObjectRepository : List<DomainObjectDataModel>, IDomainObjectRepository 
{ 
    //TODO: Implement all of the members for IDomainObjectRepository 
} 

Applicazione Progetto

public class MyApp 
{ 
    IDomainObjectRepository repository = //TODO: Initialize a concrete SqlDomainObjectRepository that loads what we need. 
    DomainObject do = repository[0]; //Get the one (or set) that we're working with. 
    do.DoSomething(); //Call some business logic that changes the state of the aggregate root. 
    repository[repository.IDof(do)] = do; //Save the domain object with all changes back to the db. 
} 

Se è necessario eseguire la transizione delle transazioni su più radici di aggregazione in modo che le modifiche vengano apportate su base tutto o niente, è necessario esaminare il modello Unità di lavoro.

Spero che questo aiuti a chiarire le cose!

+0

Grazie per la tua risposta valutabile :) – LaurinSt

+0

In genere non è necessario modificare più di un aggregato in una transazione. Vedi http://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_2.pdf. –

+1

Questo è vero in generale, ma mi piace come il documento che il tuo link rimanda a liste 4 motivi distinti sul perché potresti voler fare esattamente questo. –