Both Clean Architecture and Onion Architecture follow the same core rule:
π Dependencies always point inward toward the Domain.
Typical layers (from inside to outside):
[ Domain ] ← pure business logic
[ Application ] ← use cases
[ Infrastructure ] ← database, external services
[ Presentation ] ← controllers, API, UI
We’ll implement:
“Create a new Employee and save it.”
π Domain/
This is the center of the onion.
π HERE.
Domain objects like:
-
Employee
-
Order
-
Product
-
Customer
must live in Domain.
They must NOT depend on:
-
Database
-
Frameworks
-
Controllers
-
UI
π Domain/Entities/Employee.cs
namespace Domain.Entities
{
public class Employee
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public decimal Salary { get; private set; }
public Employee(string name, decimal salary)
{
if (salary <= 0)
throw new ArgumentException("Salary must be greater than zero");
Id = Guid.NewGuid();
Name = name;
Salary = salary;
}
public void IncreaseSalary(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Increase must be positive");
Salary += amount;
}
}
}
✔ Business rules live here
✔ No EF, no DB, no HTTP
π Domain/Interfaces/IEmployeeRepository.cs
namespace Domain.Interfaces
{
public interface IEmployeeRepository
{
void Add(Employee employee);
Employee GetById(Guid id);
}
}
IMPORTANT:
The interface lives in Domain, not Infrastructure.
Why?
Because Domain defines what it needs.
Infrastructure will implement it.
This keeps dependencies inward.
π Application/
This layer orchestrates business operations.
π HERE.
Example use case:
-
CreateEmployee
-
IncreaseSalary
-
GetEmployee
π Application/UseCases/CreateEmployee.cs
using Domain.Entities;
using Domain.Interfaces;
namespace Application.UseCases
{
public class CreateEmployee
{
private readonly IEmployeeRepository _repository;
public CreateEmployee(IEmployeeRepository repository)
{
_repository = repository;
}
public Guid Execute(string name, decimal salary)
{
var employee = new Employee(name, salary);
_repository.Add(employee);
return employee.Id;
}
}
}
✔ Uses Domain entity
✔ Uses Domain interface
✔ Does NOT know about database
π Infrastructure/
This is where:
-
EF Core
-
SQL
-
MongoDB
-
File system
-
External APIs
live.
π HERE.
π Infrastructure/Repositories/EmployeeRepository.cs
using Domain.Entities;
using Domain.Interfaces;
namespace Infrastructure.Repositories
{
public class EmployeeRepository : IEmployeeRepository
{
private readonly AppDbContext _context;
public EmployeeRepository(AppDbContext context)
{
_context = context;
}
public void Add(Employee employee)
{
_context.Employees.Add(employee);
_context.SaveChanges();
}
public Employee GetById(Guid id)
{
return _context.Employees.Find(id);
}
}
}
✔ Implements Domain interface
✔ Depends on EF
✔ Domain does NOT depend on this
Dependency direction:
Infrastructure → Domain
Correct ✅
π WebAPI/Controllers/EmployeeController.cs
π HERE (Presentation Layer)
using Application.UseCases;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/employees")]
public class EmployeeController : ControllerBase
{
private readonly CreateEmployee _createEmployee;
public EmployeeController(CreateEmployee createEmployee)
{
_createEmployee = createEmployee;
}
[HttpPost]
public IActionResult Create(string name, decimal salary)
{
var id = _createEmployee.Execute(name, salary);
return Ok(id);
}
}
✔ Controller calls Use Case
✔ Controller does NOT create Employee directly
✔ Controller does NOT talk to DB
❓ Where to define domain objects like Employee or Order?
Domain/Entities/Employee.cs
Domain/Entities/Order.cs
They contain:
- Business rules
- Validation
- Core behavior
They must NOT know:
- Database
- UI
- Frameworks
- Interface → Domain
- Implementation → Infrastructure
Domain/Interfaces/IEmployeeRepository
Infrastructure/Repositories/EmployeeRepository
- Core rules → Domain
- Use case orchestration → Application
From:
- Controllers
- API endpoints
- UI
- Background jobs
That’s the Presentation Layer.
Only this direction is allowed:
Presentation → Application → Domain
Infrastructure → Domain
π« Domain must never depend on outer layers.
Practically identical in structure.
- Onion Architecture was popularized by Jeffrey Palermo
- Clean Architecture was formalized by Robert C. Martin
Both enforce:
“Business rules should not depend on frameworks.”
No comments:
Post a Comment