2013-06-08 13 views
5

Il consiglio comunemente noto in DDD è che un Root aggregato non utilizza un servizio di dominio. Il servizio di dominio consiste nel coordinare due Root aggregati per ottenere un comportamento.Iniezione del servizio di dominio in AggregateRoots in DDD

Mi ha davvero sorpreso quando ho visto questo blog scritto da Rinat Abdullin con il titolo Building Blocks Of CQRS. Sotto la sezione Domain Service, si leggerà che un servizio di dominio viene iniettato in una radice di aggregazione.

È possibile che un root di aggregazione accetti un servizio di dominio?

risposta

4

prega, disprezzo tale articolo. È stato scritto molto tempo fa ed è assolutamente sbagliato. Se l'attuazione di un modulo con AggregateRoot e DomainService modelli, consiglio di avere una logica superiore (es gestore di richiesta) che è responsabile per:

  1. Caricamento totale i
  2. Performing calcoli con l'aiuto di servizi di dominio
  3. Mutamento dello stato aggregato di conseguenza.
+1

Rinat, non vuoi posizionare uno striscione su questi tuoi vecchi articoli, che ripeterà ciò che hai scritto nella risposta? Le persone spesso si riferiscono al tuo blog per idee e buone pratiche, ma sicuramente sai che alcune delle pratiche descritte non sono davvero le migliori ... Senza offesa, solo un suggerimento. –

3

È molto difficile aggiungere a in oggetti di dominio, e farlo è piuttosto specifico della tecnologia. In java richiede la tessitura in tempo compilato di aspetti nelle tue classi di dominio. E anche se potrei sbagliarmi su questo, penso che la maggior parte dei leader del DDD pensi che questa sia, generalmente, una cattiva idea. Entrambi lo Evans e lo Vernon lo scoraggiano attivamente e mi piace ascoltarli. Per una spiegazione completa, leggi Vernon.

+0

Sono d'accordo sul fatto che Evans e Vernon non siano d'accordo con l'idea di Rinat. Nell'esempio, utilizza un servizio di determinazione dei prezzi per trovare una soglia nel metodo LockCustomerForAccountOverdraft. Questa è una valutazione della regola aziendale che può essere eseguita inviando un comando. Con l'approccio di Evan e Verson, dovrebbe esserci un servizio di dominio di livello superiore chiamato LockCustomer in cui è possibile coordinare PricingService e CustomerAggregate. Credo che Evans e Vernon applichino SRP quindi ci sono più componenti. Rinat ha un approccio semplice, quindi può essere d'accordo con la rottura del principio aperto/chiuso in questo caso per il Cliente. –

7

In un certo senso sì. Se l'AR ha davvero bisogno di un servizio per fare il del del suo lavoro, allora lo si può iniettare come argomento del metodo. Se l'AR ha bisogno di un servizio per il più del suo comportamento, probabilmente è modellato in modo errato.

+0

Se entrambi i servizi risiedono nello stesso contesto limitato, le radici aggregate possono comunicare tra loro tramite messaggi, quindi perché è necessario iniettare un servizio? –

+1

E 'richiesto SOLO se l'AR ha bisogno di un qualche comportamento. Non vedo come la messaggistica potrebbe aiutare qui. – MikeSW

+0

Potrei aver usato il termine sbagliato qui. Intendevo eventi di dominio. –

2

Trovo abbastanza buono il following explanation. È basato sul libro di Vaughn Vernon e "inietta" il servizio di dominio nel modello di dominio attraverso la chiamata al metodo che ha effettivamente bisogno di questo servizio.

public class PurchaseOrder 
{ 
    public string Id { get; private set; } 
    public string VendorId { get; private set; } 
    public string PONumber { get; private set; } 
    public string Description { get; private set; } 
    public decimal Total { get; private set; } 
    public DateTime SubmissionDate { get; private set; } 
    public ICollection<Invoice> Invoices { get; private set; } 

    public decimal InvoiceTotal 
    { 
     get { return this.Invoices.Select(x => x.Amount).Sum(); } 
    } 

    public bool IsFullyInvoiced 
    { 
     get { return this.Total <= this.InvoiceTotal; } 
    } 

    bool ContainsInvoice(string vendorInvoiceNumber) 
    { 
     return this.Invoices.Any(x => x.VendorInvoiceNumber.Equals(
      vendorInvoiceNumber, StringComparison.OrdinalIgnoreCase)); 
    } 

    public Invoice Invoice(IInvoiceNumberGenerator generator, 
     string vendorInvoiceNumber, DateTime date, decimal amount) 
    { 
     // These guards maintain business integrity of the PO. 
     if (this.IsFullyInvoiced) 
      throw new Exception("The PO is fully invoiced."); 
     if (ContainsInvoice(vendorInvoiceNumber)) 
      throw new Exception("Duplicate invoice!"); 

     var invoiceNumber = generator.GenerateInvoiceNumber(
      this.VendorId, vendorInvoiceNumber, date); 

     var invoice = new Invoice(invoiceNumber, vendorInvoiceNumber, date, amount); 
     this.Invoices.Add(invoice); 
     DomainEvents.Raise(new PurchaseOrderInvoicedEvent(this.Id, invoice.InvoiceNumber)); 
     return invoice; 
    } 
} 

public class PurchaseOrderService 
{ 
    public PurchaseOrderService(IPurchaseOrderRepository repository, 
     IInvoiceNumberGenerator invoiceNumberGenerator) 
    { 
     this.repository = repository; 
     this.invoiceNumberGenerator = invoiceNumberGenerator; 
    } 

    readonly IPurchaseOrderRepository repository; 
    readonly IInvoiceNumberGenerator invoiceNumberGenerator; 

    public void Invoice(string purchaseOrderId, 
     string vendorInvoiceNumber, DateTime date, decimal amount) 
    { 
     // Transaction management, along with committing the unit of work 
     // can be moved to ambient infrastructure. 
     using (var ts = new TransactionScope()) 
     { 
      var purchaseOrder = this.repository.Get(purchaseOrderId); 
      if (purchaseOrder == null) 
       throw new Exception("PO not found!"); 
      purchaseOrder.Invoice(this.invoiceNumberGenerator, 
       vendorInvoiceNumber, date, amount); 
      this.repository.Commit(); 
      ts.Complete(); 
     } 
    } 
} 
+0

PurchaseOrderService è un servizio di applicazione e non un servizio di dominio, come ho capito. –

+2

@wonderfulworld: IInvoiceNumberGenerator è il servizio di dominio – mynkow

Problemi correlati