3

In Onion Architecture, we have repository layer which conducts Crud Operations (simple insert, delete, update). (Applying Entity Framework with database)

Onion Architecture

Additionally , we have Service Layer which conducts complex business logic.

Questions

(1) Should DBContext leak into Service layer? What if we wanted to conduct a complex business logic query, which is not possible from simple repository, and then EF Savechanges in database?

(2) Should IQueryable be in Service layer? Again, what if there is a need for a complex query that is best by creating and then running expression tree in the service layer after its built?

4
  • 1
    Regardless of that EF Core's DbContext support different databases or even NoSql, it still will leak database related dependencies/knowledge if you will use it in service layer or if you use IQueryable as abstraction type. Data access abstraction can have domain specific methods and types, then implementation of data access implementation will be responsible to convert database types into domain types. If you need complex query, you simply will add a another method. But try to keep as much logic in the service layer.
    – Fabio
    Sep 18, 2019 at 4:29
  • 1
    hi @Fabio feel free to write in answer, and I can send points, thanks-
    – GregDavis
    Sep 18, 2019 at 4:32
  • The answer to most questions containing the word "should" is "which way works best for you?" Sep 18, 2019 at 7:16
  • hi @RobertHarvey thought different architecture allow different things, however was curious how Onion architecture specifically talks about this issue, thanks
    – GregDavis
    Sep 18, 2019 at 17:04

1 Answer 1

1

DbContext should not leak to service layer. Service layer get data by calling repository in some in-memory objects. Then it perform the complex business logic on in-memory objects by calling some domain methods or perform itself. When the data is ready to store in external database then service layer again call repository to save the data.

Please find a sample code below.

Domain model:

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string Name
    {
        get
        {
            return $"{FirstName} {LastName}";
        }
    }

    public int Age { get; set; }
    public bool? IsAdult { get; set; }

    public void DetermineAdult()
    {
        if (Age >= 18) IsAdult = true;
        else IsAdult = false;
    }
}

User interface:

public IActionResult Index()
{
    _userService.ProcessUsers();
    return View();
}

Service layer:

// 1. Get data from repository in form of in-memory objects. 
// 2. Perform some business logic with in-memory objects.
// 3. Send in-memory objects to repository to store in database.
void IUserService.ProcessUsers()
{
    IEnumerable<User> users = _userRepository.GetAll();
    foreach (User user in users)
    {
        user.DetermineAdult(); // Calling domain method to perform the business logic.
        _userRepository.Update(user.Id, user);
    }
}

Repository layer:

IEnumerable<User> IUserRepository.GetAll()
{
    List<User> users = _userContext.Users.ToList();
    return users;
}

void IUserRepository.Update(int userId, User user)
{
    User userFromDb = _userContext.Users.FirstOrDefault(u => u.Id == userId);
    if (userFromDb == null) return;
    userFromDb.IsAdult = user.IsAdult;
    _userContext.SaveChanges();
}

Solution structure

enter image description here

Please find the full code in my github repository https://github.com/Arnab-Developer/OnionArchitecture

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.