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:
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 :-)
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. –