In this article Mark Seemann explains how Onion/Hexagonal Architecture (or Ports and Adapters) are somewhat similar to a layered architecture when the Dependency Inversion Principle (DIP) is applied. Especially if you consider the claims made in this article to hold water, I think it's all quite clear and straight-forward.
Anyway there is one quote about Ports and Adapters that made me think about the way that I structured my classes in the past
The components in the inner hexagon have few or no dependencies on each other, while components in the outer hexagon act as Adapters between the inner components, and the application boundaries: its ports.
Given I'd like to implement some app business logic, called App
hereinafter (not a too meaningful name, anyway), which would allow us to display a list of filtered employees. Displaying a list of employees would provided by a port
public interface IEmployeeListProvider
{
EmployeeList GetEmployees();
}
and persistence would be another port
public interface IEmployeeRpository
{
IEnumerable<Employee> GetAllEmployees();
void AddEmployee(Employee employeeToAdd);
void UpdateEmployee(Employee employeeToUpdate);
// further method signatures
}
Now I would implement my business logic
class App : IEmployeeListProvider
{
// most likely the filters or filter conditions would be injected.
// and the IEmployeeRepository anyway
public EmployeeList GetEmployees()
{
var employees = employeeRepository.GetAllEmployees();
var filteredEmployees = FilterEmployees(employees);
return EmployeeList.FromEnumerable(filteredEmployees);
}
private IEnumerable<Employee> FilterEmployees(IEnumerable<IEmployee> employees)
{
// elided
}
}
Basically this is how I understood Ports and Adapters as proposed by Alistair Cockburn. Anyway, this implementation somehow contradicts the Mark Seeman quote (see above), since App
depends bot on the IEmployeeRepository
and the IEmployeeListProvider
ports. Of course it would be possible to restructure the design to use a filter port
public interface IEmployeeFilter
{
IEnumerable<Employee> FilterEmployees(IEnumerable<Employee> employees);
}
and do something like this from the UI
IEmployeeFilter filter = ...; // however this is constructed
IEmployeeRepository repository = ...;
// ...
var employees = filter.FilterEmployees(repository.GetAllEmployees());
but this feels wrong to me for several reasons:
- The UI would depend on a DAL port
- We are potentially shifting logic to the UI code
- It's quite likely that UI will become a "dependency hog"
Did I get the whole quote of Mark Seeman wrong I is there any other part that I got fundamentally wrong?