7

Nella mia ultima applicazione ASP.NET MVC 2 ho cercato di mettere in pratica i concetti di Domain Driven Design (DDD), Single Responsibility Principle (SRP), Inversion of Control (IoC) e Test Driven Development (TDD). Come esempio di architettura ho seguito "Onion Architecture" di Jeffery Palermo che è stato ampliato notevolmente in ASP.NET MVC 2 in Action.Opzioni per Cablaggio automatico IoC in Domain Driven Design

Onion Architecture Diagram

Mentre, ho iniziato ad applicare con successo la maggior parte (alcuni?) Di questi principi mi manca un pezzo fondamentale del puzzle. Sto riscontrando problemi nel determinare il meccanismo migliore per il collegamento automatico di un livello di servizio alle entità del mio dominio.

Ad esempio: ogni entità di dominio che ha bisogno di inviare una e-mail dovrebbe dipendere da un'interfaccia IEmailService. Dalla mia lettura, la migliore pratica per rivelare questa dipendenza sarebbe quella di utilizzare l'iniezione del costruttore. Nel mio strato UI eseguo un'iniezione simile per implementazioni di interfaccia repository usando lo StructureMapControllerFactory da ASP.NET MVC Contrib.

Dove sono confuso è qual è il miglior meccanismo per l'auto-cablaggio dell'iniezione dei servizi necessari in entità di dominio? Le entità di dominio dovrebbero essere iniettate in questo modo? Come faccio ad usare IEmailService se non lo inserisco nelle entità di dominio?

Ulteriori domande overflow dello stack che sono grandi DDD, SRP, riferimenti CIO, TDD:

risposta

4

A meno che non sto equivoco il vostro intento e invece ho scelto per concentrarsi sulla semantica analizzerò questa affermazione "Ad esempio: ogni entità di dominio che ha bisogno di inviare una email dovrebbe dipendere da un'interfaccia IEmailService."

Devo dire che questo è su se stesso è un estremo bastardizzazione di DDD. Perché un'entità di dominio dovrebbe mai dipendere da un servizio di posta elettronica? IMO non dovrebbe. Non c'è alcuna giustificazione per questo.

Tuttavia ci sono operazioni aziendali in congiunzione con un'entità di dominio che richiederebbe la necessità di inviare e-mail. Dovresti avere la tua dipendenza IEmailService contenuta in questa classe qui, non l'entità di dominio. Molto probabilmente questa classe rientrerà in uno dei pochi nomi quasi sinonimi: Modello, Servizio o Controller a seconda dell'architettura/livello in cui ti trovi.

A questo punto il tuo StructureMapControllerFactory quindi automaticamente collegherà automaticamente tutto ciò che userebbe il IEmailService.

Mentre potrei essere minorly oltre generalizzare è praticamente una pratica standard per avere entità del dominio essere Pocos o essere quasi pocos (per evitare la violazione del SRP) però spesso SRP è violata in entità del dominio per l'amor di serializzazione e validazione. Scegliere di violare l'SRP per quei tipi di preoccupazioni trasversali è più una posizione di credenza personale che una decisione "giusta" o "sbagliata".

Come follow-up finale se la tua domanda è sulla porzione di codice che funziona veramente in un servizio stand-alone sia web o OS based e come collegare le dipendenze da quello, una normale soluzione sarebbe prendere in carico il servizio a livello di base e applicare IOC allo stesso modo simile a quello fatto da StructureMapControllerFactory in MVC. Come ottenere questo risultato dipende interamente dall'infrastruttura con cui stai lavorando.

Risposta:

Diciamo che avere IOrderConfirmService che ha un metodo EmailOrderConfirmation(Order order). Si potrebbe finire con qualcosa di simile:

public class MyOrderConfirmService : IOrderConfirmService 
{  
    private readonly IEmailService _mailer; 

    public MyOrderConfirmService(IEmailService mailer) 
    {   
     _mailer = mailer;   
    } 

    public void EmailOrderConfirmation(Order order) 
    {   
     var msg = ConvertOrderToMessage(order); //good extension method candidite 
     _mailer.Send(msg);   
    }  
} 

si dovrebbe quindi avere la vostra classe OrderController che sarebbe qualcosa di simile

public class OrderController : Controller 
{  
    private readonly IOrderConfirmService _service; 

    public OrderController(IOrderConfirmService service) 
    {   
     _service= service;   
    } 

    public ActionResult Confirm() 
    {  
      _service.EmailOrderConfirmation(some order); 

      return View(); 
    }  
} 

StrucutreMap sarà intrinsecamente costruire sei tutta la catena di architettura quando si utilizza il costruttore iniezione correttamente. Questa è la differenza fondamentale tra accoppiamento stretto e inversione del controllo. Quindi, quando StructureMapFactory va a creare il controller, la prima cosa che vedrà è che ha bisogno di IOrderConfirmService. A questo punto controllerà se è in grado di collegare direttamente IOrderConfirmService, il quale non può perché ha bisogno di IEmailService. Quindi controllerà se può collegare IEmailService e per argumentsake diciamo che può farlo. Quindi a questo punto costruirà EmailService, che quindi creerà MyOrderConfirmService e inserirà EmailService, quindi creerà OrderController e inserirà MyOrderConfirmService. Da qui deriva il termine inversione di controllo. StructureMap costruirà l'EmailService per primo nell'intera catena di dipendenze e terminerà per ultimo con il Controller. In una configurazione strettamente accoppiata questo sarà l'opposto in cui il Controller sarà costruito per primo e dovrà costruire il servizio aziendale e quindi costruire il servizio di posta elettronica. Il design strettamente accoppiato è molto fragile rispetto al CIO.

+0

Chris sei morto. Mi sembrava di aver violato qualcosa iniettando "IEmailService" in entità di dominio. Supponendo che si tratti di un servizio di base, come/dove devo collegare le varie entità che dovranno utilizzare 'IEmailService'? Immagino di avere difficoltà a visualizzare come funzionerebbe. – ahsteele

+0

Ha risposto al tuo commento. –

+0

Ho apportato una piccola modifica al codice del costruttore del controller. – ahsteele