Question

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?

Was it helpful?

Solution

It doesn't contradict the statement. Conceptually, your business object, App, provides "ports" (the two interfaces) so that you can hook up other components on top. The two interfaces are owned by the App class. Yes, it depends on those interfaces, but it does not depend on the components that implement or use them - and that's exactly the point. IEmployeeRepository is a so called required interface (the App class requires that other components implement this interface in order to be able to work with the App class), and IEmployeeListProvider is a provided interface (provided by the App class, for other components to use). You should view the three types (App, IEmployeeRepository, IEmployeeListProvider) as a single thing (and you would put them in the same package). Then, the other components that implement or use these interfaces depend on them (and, by extension, on the whole triplet).

That's what (the first part of) the dependency inversion principle states: "High-level modules should not depend on low-level modules. Both should depend on abstractions." Here, your high-level module is the App class, and one of the low-level modules would be, say, the repository. The abstraction they both depend on is the IEmployeeRepository interface, but the key thing is that this interface is owned by the App class, not by the implementation of the repository. So to determine the direction of the dependency, you look at the direction of the arrow between the implementation of the repository, and the App + IEmployeeRepository pair.

OTHER TIPS

I think that your point of view of Ports & Adapters is ok. That's mine too.

In his article, Mark Seemann talks about two hexagons. I don't agree, I think there's just one hexagon (the inner). The inner hexagon is the app, and the sides of the inner hexagon are the ports. These ports are interfaces that belong to the inner hexagon, in order to communicate with the outside world (the outside of the inner hexagon).

There are two kinds of ports:

-Drivers: they offer the app functionality to the outside world (use case boundary)

-Driven: they abstract what the app needs from the outside world (store/retrieve data from a datastore, send a notification, etc).

In your case, your "IEmployeeListProvider" interface is a driver port. The implementation of this port lives inside the app (the inner hexagon). This implementation, in order to do its task, needs to access the database (outside world), so it uses a driven port (your "IEmployeeRpository" interface).

There are also two kinds of adapters (outside the inner hexagon):

-Drivers: they take the request of a client that wants to use the app, and they call a driver port interface.

-Driven: they implement a driven port in order to use a resource needed by the app.

So, my understanding of P&A architecture is similar to yours, I don't think you are wrong.

I don't agree with Seemann's article. He considers that ports are the boundaries of "his" outer hexagon, and the adapters connect those ports with the inner hexagon. For me, the ports are the boundaries of the inner hexagon, and the adapters connect the ports with the "things" (humans,web,databases,etc) from the outside world that either wants to use the app (drivers) or are used by the app (driven). Going one step further, Alistair Cockburn, split the driven "things" in two kinds: repositories and recipients.

See this talk called "Alistair in the Hexagone":

https://www.youtube.com/watch?v=th4AgBcrEHA

https://www.youtube.com/watch?v=iALcE8BPs94

https://www.youtube.com/watch?v=DAe0Bmcyt-4

Licensed under: CC-BY-SA with attribution
scroll top