2015-11-08 11 views
5

Attualmente sto rifattorizzando un progetto ASP.NET MVC per utilizzare lo onion arcitecture poiché sembra che sia adatto alle esigenze di sviluppo futuro.Posizionamento di modelli di vista/DTO nell'architettura a cipolla

Ho installato gli strati che penso che ho bisogno di usare e la mia soluzione ora assomiglia a questo:

enter image description here

Quindi, in sostanza, come ho capito, il progetto ClientName.Core non dovrebbe avere alcun riferimenti ad altri progetti. Lo ClientName.Infrastructure dovrebbe avere un riferimento allo ClientName.Core. La cartella Interfacce su ClientName.Core definisce i servizi che si trovano nel progetto ClientName.Infrastructure e il mio DbContext e le entità di dominio sono separati, quindi solo le entità si trovano nel progetto principale.

Dove ho fatto scorrere la testa contro il muro, il progetto ClientName.Infrastructure non deve restituire le entità di dominio al client che lo chiama. Ciò creerebbe un riferimento tra il progetto principale e qualsiasi interfaccia utente che "viola" il principio della cipolla. Un modo per aggirare questo, come ho letto, è quello di rendere i servizi di infrastruttura restituire DTO invece. Tuttavia, se sto restituendo un PersonDto dalla classe PersonService, l'oggetto PersonDto deve essere conosciuto dal progetto ClientName.Core poiché è lì che si trova l'interfaccia.

Quindi la domanda è: dove esattamente posiziono il DTO/ViewModel/altri modelli usati per l'interfaccia utente/client? Creo una libreria di classi separata che contiene questi modelli e fa riferimento sia all'interfaccia utente, all'infrastruttura e ai progetti principali?

Qualsiasi aiuto/suggerimento notevolmente è apprezzato come io sono un po 'confuso su questo ;-)

Grazie in anticipo.

EDIT

Sulla base di Euphorics risposta, sto scrivendo Codice di esempio qui solo per controllare se ho capito bene e magari con alcune domande di follow-up.

Quindi, fondamentalmente, a mio ClientName.Core strato, ho la mia entità che contiene la logica di business, vale a dire un Person e un'entità Firm potrebbe essere la seguente:

(Exists in ClientName.Core/Entities) 
public class Person 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public Firm Firm { get; set; } 
} 

(Exists in ClientName.Core/Entities) 
Public class Firm 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public IEnumerable<Person> Employees { get; set; } 

    public IEnumerable<Person> GetEmployeesByName(string name) 
    { 
     return Employees.Where(x => x.Name.Equals(name)); 
    } 

    public void AddEmployee(Person employee) 
    { 
     Employees.Add(employee); 
    } 

    public void CreateFirm(Firm firm) 
    { 
     // what should happen here? Using entity framework, I have no reference to the DbContext here... 
    } 
} 

(Exists in ClientName.Infrastructure/Services) 
public class FirmService : IFirmService 
{ 
    private readonly IDbContext _context; 

    public FirmService(IDbContext context) 
    { 
     _context = context; 
    } 

    public Firm GetById(int firmId) 
    { 
     return _context.Firms.Find(firmId); 
    } 

    // So basically, this should not call any domain business logic? 
    public void CreateFirm(CreateFirmFormViewModel formViewModel) 
    { 
     Firm firm = new Firm() 
     { 
      Name = formViewModel.Name; 
     } 

     _context.Firms.Add(firm); 
     _context.SaveChanges(); 
    } 

    public IEnumerable<Person> GetEmployeesByName(int firmId, string name) 
    { 
     Firm firm = _context.Firms.Find(firmId); 
     return firm.GetEmployeesByName(name); 
    } 
} 

Sono corretto che ogni lettura query dovrebbe essere definito direttamente sull'entità (forse come estensione di un'entità dal momento che sto utilizzando Entity Framework) e qualsiasi creazione/aggiornamento/eliminazione si verificherebbe solo nei servizi nel livello infrastruttura?

Se i metodi di lettura (vale a dire il metodo IEnumerable<Person> GetEmployeesByName(int firmId, string name)) si trovano anche nell'interfaccia/classe FirmService?

Grazie ancora :-)

risposta

2

In primo luogo, si sono non tornare Person da PersonService, ma da IPersonService. Questa è un'enorme differenza concettuale. PersonService implementa solo ciò che definisce IPersonService. Non si cura di quali interfacce o oggetti utilizza.

In breve, il progetto Core dovrebbe includere tutta la logica aziendale nelle entità aziendali. Che se non si dispone di una logica effettiva, sarebbe semplicemente DTO.Quindi, il Infrastructure sarebbe responsabile del salvataggio/caricamento di tali entità dal database. Se crea il proprio modello con le proprie entità o se riutilizza quelle definite dal core è il dettaglio dell'implementazione di Infrastructure. Allo stesso tempo, il progetto UI (o Web) funzionerà con le entità nel progetto Core, ma non gli interesserà come avviene la persistenza. Vuole solo "fare affari".

Il punto chiave qui è che il Core (o la logica aziendale) è auto-testabile senza coinvolgere UI o codice di database. Finché sei in grado di farlo, non importa dove sono alcuni DTO.

Edit:

Questo sta cominciando a ottenere duro. Stai riscontrando requisiti di progettazione in conflitto. Da un lato, hai un design di dominio chiaro. Dall'altra, vuoi che il tuo modello sia persistibile usando EF. Una cosa è chiara: stai andando a compromettere e distorcere il tuo modello di dominio in modo che possa essere utilizzato in EF.

Ora per problemi concreti. Il primo è la creazione dell'azienda. Qui, hai un conflitto tra "nuova impresa" come inteso dal dominio. Per esempio. assicurandosi che la nuova ditta abbia un nome valido. Dall'altro, si desidera creare l'istanza e renderne l'EF consapevole. Il modo in cui lo farei è rimuovere la classe Firm dalla Firm e cambiare in IFirmService in modo che accetti solo i parametri necessari per creare nuovo Firm (ad esempio solo il nome) e restituire la ditta appena creata. Inoltre, l'idea che il repository non dovrebbe chiamare il dominio è sbagliata. Ad esempio, il repository potrebbe chiamare il dominio per convalidare se la ditta creata è valida e in caso contrario, non aggiungerla in EF.

Il secondo numero che vedo è il mantenimento della collezione Dipendenti in Azienda. Anche se questo è ottimo per il dominio, è un disastro per la persistenza (a meno che tu non pensi che il caricamento lento vada bene in questo scenario). Ancora. Non esiste un modo semplice per soddisfare il requisito di dominio di "La società ha una collezione di dipendenti" e la capacità di mantenere l'entità nel database. La prima opzione non è quella di avere questa raccolta e spostarla nello IFirmService, dove può essere implementata correttamente. La terza opzione è la più estrema. Ed è quello di rendere Firm una classe astratta mentre si fanno tutti "ottenere" un metodo astratto, crearne un'implementazione nel progetto di infrastruttura e implementare i metodi get usando il contesto EF che l'istanza concreta avrebbe. Inoltre, ciò potrebbe essere possibile solo se nell'infrastruttura viene creata l'istanza concreta di Firm, che è ciò che ho suggerito sopra.

Personalmente mi piace la terza opzione, perché mantiene i metodi belli e coesi. Ma alcuni puristi di EF potrebbero non essere d'accordo con me.

+0

Ciao @Euphoric, grazie per la tua risposta. Ho appena aggiornato la mia domanda con un semplice codice e alcune domande di follow-up, giusto per assicurarmi di aver capito bene. –

Problemi correlati