10

SfondoDove definire le interfacce per un repository in un'architettura a livelli?

Sto cercando di creare una semplice applicazione per capire davvero l'intero stack di DDD + TDD + ecc. Il mio obiettivo è quello di iniettare dinamicamente le classi del repository DAL in fase di runtime. Ciò mantiene i miei livelli di dominio e servizi applicativi testabili. Ho intenzione di utilizzare "DI dei poveri" per realizzare questo per ora ... così vorrei fare questo in una semplice applicazione console nei pressi di avvio:

 

    // Poor man's DI, injecting DAL repository classes at runtime 
    var productRepository = new SimpleOrder.Repository.ProductRespository(); 
    var customerRepository = new SimpleOrder.Repository.CustomerRepository(); 
    var orderRepository = new SimpleOrder.Repository.OrderRepository(); 

    // Constructor injection into this class in the Application Services layer, 
    // SimpleOrder.ApplicationFacade 
    OrderEntry oe = new OrderEntry(customerRepository, orderRepository, productRepository); 

per raggiungere questo obiettivo l'iniezione di dipendenza, ho creato tre repository interfacce:

 
-- ICustomerRepository 
-- IOrderRepository 
-- IProductRespository 

Una tipica applicazione:

 

    namespace SimpleOrder.Domain.Interfaces 
    { 
     public interface ICustomerRepository 
     { 
      Customer GetCustomerById(int customerId); 
      void SaveCustomer(Customer customer); 
     } 
    } 

** Si noti che SaveCustomer fa riferimento alla classe del modello cliente definita nella doma a livello. Questo è tipico degli altri repository.

Comunque non sono sicuro di quale progetto/strato di essi dovrebbero essere attuati in Ho 5 progetti in una soluzione:

  1. SimpleOrder.ConsoleClient (presentazione) - voglio iniettare. l'attuazione specifica del dominio da qui l'applicazione

  2. SimpleOrder.ApplicationFacade (servizi applicativi) - grosso di livello superiore, i metodi più grossolani a grana o rchestrating metodi di livello inferiore nel dominio

  3. SimpleOrder.Contracts - classi DTO utilizzati per la comunicazione tra la presentazione e applicazione dei servizi

  4. SimpleOrder.Domain (dominio/BLL) - dominio classi del modello Cliente, ordine, OrderItem, prodotto

  5. SimpleOrder.Repository (dAL) - implementa il repo interfacce sitory

Qui sono le mie opzioni come la vedo io:

Opzione 1: definire le interfacce repository in SimpleOrder.Contracts ...

PRO: è qui che penso dovrebbero appartenere perché l'ho creato per condividere i contratti tra varie preoccupazioni/livelli. ex., DTOs sono definiti qui.

CON: tuttavia, le firme di metodo in ogni interfaccia fanno riferimento a classi di modelli di dominio.
Ciò significa che dovrei aggiungere un riferimento a SimpleOrder.Domain, ma quando ilSimpleOrder.I contratti sono referenziati in un altro progetto, sarà necessario trasportare SimpleOrder.Domain per il viaggio. Questo non mi sembra giusto.

Opzione 2: stesso scenario come sopra, ma ho anche definire le interfacce per ciascun dominio modello di classe nei SimpleOrder.Contracts in modo da poter rompere l'accoppiamento delle interfacce repository alle classi del modello attuale.

Esempio:

 

    namespace SimpleOrder.Domain.Interfaces 
    { 
     public interface ICustomerRepository 
     { 
      ICustomer** GetCustomerById(int customerId); 
      void SaveCustomer(ICustomer customer); 
     } 

     public interface ICustomer 
     { 
      int CustomerId { get; set; } 
      string Name { get; set; } 
      System.Collections.Generic.List Orders { get; } 
     } 
    } 

IMPATTO: Ogni classe modello di dominio avrebbe dovuto implementare la sua interfaccia correlata. Ad esempio,

 

    public class Customer : SimpleOrder.Domain.Interfaces.ICustomer 
    { 
     public Customer() 
     { 
      _orders = new List(); 
     } 

     public int CustomerId { get; set; } 
     public string Name { get; set; } 

     private List _orders; 
     public virtual List Orders { 
      get { return _orders; } 
     } 
    } 

PRO: risolve il problema dell'opzione 1.

CON: esplode il numero di file (e la complessità percepita) nel progetto perché ogni classe di dominio ha ora un'interfaccia associata.

Opzione 3: Definire le Interfacce repository nel SimpleOrder.Domain

IMPATTO: Al fine di iniettare le classi repository di cemento nello strato servizi applicativi (progetto SimpleOrder.ApplicationFacade) dal SimpleOrder.ConsoleClient in fase di esecuzione , SimpleOder.ConsoleClient avrà anche bisogno di un riferimento a SimpleOrder.Domain.

PRO: Questo risolve anche l'opzione 1

CON: Stavo cercando di evitare di riferimento al livello di dominio dal livello di presentazione direttamente perché ora il livello di presentazione può conoscere troppo circa il livello di dominio. Quando sostituirò l'applicazione console in futuro con un'app WPF o ASP.NET MVC in futuro, rischio che le implementazioni secondarie e successive del livello di presentazione provino a chiamare metodi nel modello anziché nel livello Application Services. (Tuttavia lo considero nell'opzione 4.)

Opzione 4: Inserire le interfacce in SimpleOrder.Domain, quindi fare riferimento a SimpleOrder.Domain da SimpleOrder.ConsoleClient.

PRO: risolve tutti i problemi sopra indicati.

CON: Questo non mi sembra giusto, perché sarei fornire l'accesso dal livello di presentazione direttamente ai metodi di livello inferiore nello strato di dominio quando dovrei solo fornirò l'accesso al livello superiore grosso metodi in SimpleOrder.ApplicationFacade.

DOMANDA Ho provato ciascuno di questi, ma ho optato per l'opzione 4, tuttavia, che lascia l'amaro in bocca su di esso. C'è un'opzione migliore? Sono sulla strada giusta qui?

+0

A un esame più attento ... le opzioni 3 e 4 sono fondamentalmente le stesse. Ops. Dovrei avere una lettura delle prove più da vicino. –

risposta

9

Da quanto ho capito della tua domanda, sono d'accordo che l'opzione 4 sia la migliore. Le interfacce del repository devono essere dichiarate nel livello dominio accanto a tutti gli oggetti dominio. L'implementazione di dette interfacce dovrebbe far parte del livello dell'infrastruttura, il livello che collega il livello del dominio al mondo. Dai uno sguardo allo Hexagonal Architecture per vedere alcune delle motivazioni per questo.

Per risolvere il problema dell'opzione 4, non si dovrebbe pensare all'app della console come al solo livello di presentazione. Ha anche altre responsabilità, come l'host per l'applicazione e lo composition root in termini DI. Potrebbe esserci un componente di presentazione dell'app console che comunica solo con i servizi dell'applicazione. È anche possibile incapsulare il servizio applicativo dietro a un open host service implementato con Web API ASP.NET. Quindi il livello di presentazione farebbe riferimento solo a questo servizio e sarà nascosto dal livello del dominio sottostante.

+0

Sulla base della tua risposta alla mia domanda precedente e ora questa, sembra che ti debba un caffè o una bistecca o qualcosa del genere. :) Ho un rapido follow-up ... hai scritto "Da quello che ho capito della tua domanda" ... ammettiamolo, è stato a lungo finito. C'era qualcosa di terminologico o di pensiero-saggio che pure suonava di traverso? GRAZIE per la tua grande risposta ... di nuovo. –

+0

Quello che volevo dire è che non sono a conoscenza di tutti i dettagli del progetto, quindi potrei aver perso un vincolo. Ma una domanda molto ben dichiarata comunque! – eulerfx

0

Sono d'accordo con eulerfx. L'unica cosa che vorrei aggiungere è che non è giusto perché l'architettura tradizionale a più livelli ti farebbe dipendere tutto dal database e usare le astrazioni per rompere queste dipendenze.

Si dovrebbe guardare a Onion Architecture che è un esempio del modo in cui si organizzano le dipendenze nell'opzione 4. Personalmente, credo che questo sia il modello di architettura più scalabile disponibile. Se abbini questa architettura alle pratiche DDD, ottieni la massima flessibilità con il minimo sforzo.

In una nota a margine, molte implementazioni che ho visto estrapolano i contratti per i repository e i servizi di dominio nel proprio progetto. Questa è puramente una decisione organizzativa però. È perfettamente valido averli nel progetto del tuo modello di dominio.

+0

FYI si può considerare che l'architettura della cipolla sia essenzialmente la stessa cosa dell'esagonale; alcuni lo considerano una ripugnanza poiché l'esagonale era delineato qualche tempo prima della cipolla. – eulerfx

+0

Capisco cosa intendi, grazie per l'informazione. Perché nessuno dei due modelli di architettura descrive la dipendenza tra interfaccia utente/applicazione/infrastruttura? Facendo riferimento alla tua risposta, l'Applicazione ha la responsabilità di scegliere quale implementazione di Infrastructure utilizzare, in quanto ciò non rientra nello scopo di ciò che stanno cercando di esprimere con questi modelli o c'è un altro motivo per lasciare questo fuori dai modelli di architettura? Mi rendo conto che non hai creato i modelli; Mi sto solo chiedendo la tua opinione su questo. –

+0

In esagonale, l'interfaccia utente è solo un altro adattatore e può essere considerata un'infrastruttura. Forse ho frainteso quello che hai chiesto? A quale dipendenza tra UI/App/Infrastruttura ti riferisci? Ci sono alcune prospettive su questo che vedo. – eulerfx