I'm currently refactoring a ASP.NET MVC project to use the onion arcitecture since it seems that it suits the needs of future development.
I have set up the layers which I think I need to use and my solution now looks like this:
So, basically as I've understood, the ClientName.Core
project should not have any references to other projects at all. The ClientName.Infrastructure
should have a reference to the ClientName.Core
. The Interfaces folder on ClientName.Core
defines the services which is in the ClientName.Infrastructure
project and my DbContext and domain entities are separated so only the entities are in the core project.
Where I ran my head against the wall was, that the ClientName.Infrastructure
project should not return domain entities to what-ever client that is calling it. This would create a reference between the core project and any UI which "violates" the onion principle. A way around this, as I've read, is to make the infrastructure services return DTO´s instead. However, if I'm returning i.e. a PersonDto
from the PersonService
class, the PersonDto
object needs to be known by the ClientName.Core
project since that is where the interface is.
So the question is: where exactly do I place the DTO/ViewModel/other models used for the UI/client? Do I create a separate class library which holds these models and let both the UI, infrastructure and core projects reference it?
Any help/hint is greatly appreciated as I'm a bit confused about this ;-)
Thanks in advance.
EDIT
Based on Euphorics answer, I'm writing up example code here just to check if I got it right and maybe with some follow-up questions.
So basically, in my ClientName.Core
layer, I have my entities which contains business logic, i.e. a Person
and a Firm
entity could look like this:
(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);
}
}
Am I correct that every read-query should be defined directly on the entity (maybe as a entity extension since I'm using Entity Framework) and any create/update/delete would happen only in the services in the infrastructure layer?
Should the read (i.e. the IEnumerable<Person> GetEmployeesByName(int firmId, string name)
method) methods also be on the FirmService
interface/class?
Thanks again :-)