On methane there is an example of onion architecture. There was a question about this architecture. There, in the example, the infrastructure.Business layer is created and links to domain.core and Services.Interfaces are connected to this project. And is it considered normal to connect to the infrastructure.Business layer, the Infrastructure.Data layer, and that the business infrastructure have access to the context of the application data and, accordingly, the unit of work class, i.e. work directly with the base? Or is it a bad tone for this architecture?

enter image description here

This is the structure of my application at the moment.

Here is a specific project responsible for receiving data from the database.

enter image description here

UnitOfWork class:

public class UnitOfWork : IDisposable { private Context db = new Context(); private ClientRepository clientRepository; public ClientRepository Clients { get { if (clientRepository == null) clientRepository = new ClientRepository(db); return clientRepository; } } public void Save() { db.SaveChanges(); } private bool disposed = false; public virtual void Dispose(bool disposing) { if (!disposed) db.Dispose(); disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } 

Client repository:

 public class ClientRepository : IRepository<Client> { private Context db; public ClientRepository(Context context) { db = context; } public IEnumerable<Client> GetAll() { return db.Clients.ToList(); } public IEnumerable<dynamic> GetFieldsValue(Func<Client, Boolean> predWhere, Func<Client, dynamic> predSelect) { return db.Clients.Where(predWhere).Select(predSelect).ToList(); } public Client Get(int id) { return db.Clients.Find(id); } public void Create(Client client) { db.Clients.Add(client); } public void Update(Client client) { db.Entry(client).State = EntityState.Modified; } public void Delete(int id) { Client client = db.Clients.Find(id); if (client != null) db.Clients.Remove(client); } } 

Client domain model from the Domain.Core project:

 public class Client { public int Id { get; set; } [Required] [Display(Name = "Фамилия")] public string CSurname { get; set; } [Required] [Display(Name = "Имя")] public string CName { get; set; } [Required] [Display(Name = "Отчество")] public string CPatronymic { get; set; } [Required] [Display(Name = "Логин")] public string Login { get; set; } [Required] [Display(Name = "Пароль")] public string Password { get; set; } [Required] [Display(Name = "E-meil")] public string Email { get; set; } public virtual ICollection<Order> Orders { get; set; } public Client() { Orders = new List<Order>(); } } 

Repository Interface:

 public interface IRepository<T> where T : class { IEnumerable<T> GetAll(); T Get(int id); IEnumerable<dynamic> GetFieldsValue(Func<T, Boolean> predWhere, Func<T, dynamic> predSelect); void Create(T item); void Update(T item); void Delete(int id); } 
  • Here is a good lecture on this topic. - Bulson pm
  • @Bulson I really like the approach suggested in the video, so I often mention it. However, it’s time to quote one of the comments below: "There is a high dependency to the persistence layer . I’ve got it done on 29:45, but still not a purely" clean "application layer. " - AK

1 answer 1

I will not hide behind the fact that the "norm" is different: if you want the "right" onion architecture, then we will talk about it.

Dependencies in the source code should be directed inward, in the direction of high-level policies.

By definition, a module is a “top” level if it does not depend on other modules. Low-level modules are those that depend on a large number of other modules and the more modules from which there are dependencies, the lower the module in the hierarchy.

So, as soon as you want your high-level module to connect / know about the lower modules - you violate the principle that the modules of the lower levels should depend on the modules of the upper levels, and not vice versa.

In the business layer, you need to know that there is an infrastructure layer, but the infrastructure is a very volatile module (the more the variability is, the lower the module), so the overlying layers should not depend on it.

All of the above can be read in more detail with Robert Martin in the book "Pure Architecture", one of the must read books for the developer. It will show where the principle of dependency inversion comes from (for architecture at the class level), it tells about stable abstractions and specific components, complete and incomplete architectural boundaries and how they look, and also shows the principle at the level of large modules. I really, really recommend reading this book.

How to do "on science"? Declare interfaces in high-level projects, implement them in the underlying projects, when you start the application, declare how you will resolve the dependencies.

  • AK is obtained by adding a link to the Infrastructure. Business layer on the layer Infrastructure.Data layer in which there is access to the database, will this make the business layer lower? But after all, in fact, this and that layer of infrastructure, or am I wrong? - Andrei
  • @Andrey Do you have two projects in the infrastructure layer? This means that one of them is of a higher level, the second is of a lower level. It could be three and five, this business would not change. Read about dependency inversion in the book: a low-level layer calls code from a higher level, but a high-level module should not include code from lower-level modules. This gap is made precisely through the interface declaration and its implementation. - AK
  • AK, yes, there are 2 projects, but it turns out that in my business logic layer there will be no direct access to the database, and I will have to transfer ready-made models or just data from the database to the business logic layer, and I have a problem with getting the data from bases on the business layer, and because of this, this question has appeared. - Andrei
  • AK is a living example of my problem, this is what a login form is. I enter the username and password. And there is a problem where to get the user with this login and password, and check the correctness of the password and login? Immediately in the controller? It seemed to me that the controller should not be responsible for this. Then further question. If you make the business logic interface and then you have to transfer the infrastructure.business layer from the controller to the client itself, but if for example the access to the database is available at the business layer, then I could get the client there and check the entered parameters. - Andrei
  • @Andrew An example for entering the system not from this opera. If you read the literature, then many correct books say that authentication / authorization is a thing for the entire application, it can be present in any form on ALL application layers. Another example of end-to-end functionality is logging. Therefore, let's not discuss this example in this question, okay? For that phrase where "yes 2 projects" - and let you add a question with a print screen of the project structure and describe the example of retrieving data from the database in a more extensive way. Straight with the classes to be specific. - AK