CodeBlog.xyz

Onion Architecture – concepts

September 1, 2023 | by Meir Achildiev

Onion Architecture – concepts

Conceptual Understanding

  1. Onion Architecture is about the positioning of concerns and flow of dependencies, primarily toward the center. Dependencies flow inward, not outward.
  2. The center of the onion, the Domain layer, should have no dependencies on any outer layers. It defines the business rules and data.
  3. The layers include: Domain Model (center), Application Layer, Infrastructure (outermost).
  4. The inner layers define interfaces that outer layers implement, leading to an architecture that is easy to maintain, easy to test, and less coupled.
  5. The infrastructure is just a detail and the application should not be dependent on it.

Domain Layer

  1. This is the innermost layer representing enterprise-wide business concepts.
  2. It includes Entities, Value Objects, and Domain Events.

Example 1:

//Entity
public class User
{
    public Guid Id { get; private set; }
    public string Email { get; private set; }
    public string Name { get; private set; }

    private User() { }

    public User(string email, string name)
    {
        //business rules go here
        if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(name))
            throw new ArgumentException("Email and Name cannot be null or empty.");

        Id = Guid.NewGuid();
        Email = email;
        Name = name;
    }
}
  1. It is technology-agnostic and has no dependencies on outer layers.
  2. Contains business rules that should be common to multiple applications in the same domain.
  3. Defines interfaces for repositories that outer layers will implement.

Example 2:

//Interface for repository
public interface IUserRepository
{
    Task AddAsync(User user);
    Task<User> GetByEmailAsync(string email);
}

Application Layer

  1. Contains use case specific business rules.
  2. This layer orchestrates the domain layer and the infrastructure layer.
  3. It’s responsible for calling infrastructure layer interfaces like repository interfaces or infrastructure services.
  4. It communicates with the domain layer using interfaces.
  5. Services in this layer serve as the entry point to the application.

Example 3:

public class UserService
{
    private readonly IUserRepository _userRepository;

    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public async Task CreateUserAsync(string email, string name)
    {
        var existingUser = await _userRepository.GetByEmailAsync(email);

        if (existingUser != null)
            throw new ArgumentException("User with the same email already exists.");

        var user = new User(email, name);

        await _userRepository.AddAsync(user);
    }
}

Infrastructure Layer

  1. This is the outermost layer.
  2. It provides concrete implementations for interfaces defined in the inner layers, like database repositories, file system interactions, network communication, etc.
  3. It communicates with the inner layers through interfaces.
  4. This layer should be easily replaceable.
  5. It may contain implementations of domain services if they involve infrastructure concerns.

Example 4:

public class UserRepository : IUserRepository
{
    private readonly MyDbContext _context;

    public UserRepository(MyDbContext context)
    {
        _context = context;
    }

    public async Task AddAsync(User user)
    {
        await _context.Users.AddAsync(user);
        await _context.SaveChangesAsync();
    }

    public async Task<User> GetByEmailAsync(string email)
    {
        return await _context.Users.FirstOrDefaultAsync(u => u.Email == email);
    }
}

In this example, MyDbContext would be your Entity Framework Core DbContext, which is a part of the Infrastructure layer and provides a gateway to your database.

Architectural Advantages

  1. High level of maintainability due to the decoupled nature of the architecture.
  2. Highly testable as dependencies can be easily mocked.
  3. Independent of technology, making it flexible and adaptable to changes.
  4. Changes in the database or UI do not affect the core logic of the application.

Architectural Disadvantages

  1. It can be an overkill for small applications, adding unnecessary complexity.
  2. Can be time-consuming to set up due to the strict separation of concerns.

The Onion Architecture promotes separation of concerns, maintainability, and testability, making it a great choice for large and complex applications. However, always analyze your project’s requirements to ensure that this architectural style is a suitable choice.

RELATED POSTS

View all

view all