Ho assegnato un progetto POC a qualcuno in cui ho chiesto di implementare sia la segregazione sulla responsabilità della query dei comandi, l'inversione del controllo (con iniezione delle dipendenze) e il modello del repository. "Qualcuno" mi ha dato un progetto di soluzione POC ma non sono sicuro se questo è il modo in cui è fatto. Qui parlerò del progetto POC
- Il progetto è una semplice applicazione a 3 livelli: Presentation Layer (PL), Business Logic Layer (BLL) e Data Access Layer (DAL); ogni livello è un progetto separato
- Il Presentation Layer è un'applicazione Web, il BLL e il DAL sono progetti di librerie di classi
- Nel livello aziendale, sono definite interfacce del repository. Il riferimento della libreria BLL viene aggiunto al progetto DAL e all'interno del progetto DAL ci sono classi concrete che implementano le interfacce del repository. Ecco come viene applicata l'opzione Inversion of Control
- Poiché viene eseguito Comando-Query-Responsabilità-Segregazione , le interfacce del repository nel Business Layer dichiarano solo i metodi Aggiungi / Aggiorna ed Elimina. Per leggere, ci sono interfacce "Lettura" direttamente nel DAL e nel DAL ci sono classi concrete che implementano queste interfacce.
- Il Presentation Layer contiene riferimenti sia alla libreria BLL che alla libreria DAL. Le chiamate a Aggiungi / Aggiorna / Elimina vengono instradate tramite BLL al DAL mentre tutte le letture vengono eseguite direttamente dal DAL. Credo che questo sia conforme al concetto Command-Query-Responsibility-Segregation di bypassare la BLL per fare letture.
Ecco un'illustrazione di come tutto questo è stato impostato. Ci sono tre progetti
- NW.Web
- NW.Business
- NW.DataAccess
Di seguito è riportata un'istantanea del codice nei diversi livelli.
- NW.Web -
// A class in the Presentation Layer
public class CustomerPage
{
// Business layer Interface from NW.Business namespace
private ICustomerBusiness ICustB;
//DAL Read interface from NW.DataAccess.Read namepsace
private ICustomerRead<Guid> ICustR;
//Constructor for the Customer Page that uses Constructor Injection
public CustomerPage(ICustomerBusiness ICustB, ICustomerRead<Guid> ICustR)
{
this.ICustB = ICustB;
this.ICustR = ICustR;
}
}
- NW.Business -
//Declaration of business interface in the Business Layer
interface ICustomerBusiness
{
void Persist();
}
// A class in the Business Layer that implements the business interface
public class Customer: ICustomerBusiness
{
//Repository interface object that will be injected by Constructor Injection.
private ICustomerRepository ICustRep;
public Customer(ICustomerRepository ICustRep)
{
this.ICustRep = ICustRep;
}
public void Persist()
{
ICustRep.AddOrUpdate();
}
}
//Declaration of Repository interface in the Business Layer
public interface ICustomerRepository
{
void AddOrUpdate();
void Delete();
}
- NW.DataAccess -
public class CustomerRepository : ICustomerRepository
{
public void AddOrUpdate()
{
//implementation of Add or Update
}
public void Delete()
{
//implementation of Delete
}
}
//A Read interface in the Data Access Layer
interface ICustomerRead<T>
{
// A read is returned as DTO since in Database this may map to more than 1 table
CustomerDTO GetCustomerDetails(T id);
}
// An implementation of the Read Interface in the Data Access Layer
namespace NW.DataAccess.Read
{
public class CustomerRead<T> : ICustomerRead<T>
{
public CustomerDTO GetCustomerDetails(T id)
{
//implementation here
}
}
}
Il mio istinto è che qui c'è qualcosa di sbagliato. Sembra CQRS o almeno l'implementazione di cui sopra non risponde ad alcuni requisiti
- L'oggetto Business del cliente (classe Customer) potrebbe dover leggere dal database per scopi interni (come l'inizializzazione delle variabili, ecc.). Con la lettura definita direttamente nel livello DAL, l'unico modo per farlo sarebbe quello di fare riferimento alla dll DAL nella BLL. Ma ciò creerebbe un riferimento circolare e andrebbe contro lo IOC che è stato fatto
- Che cosa accade quando esiste un requisito di lettura comune a tutti gli oggetti di business?