1

In Ports and Adapters (Hexagonal) Architecture you have:

DrivingAdapter -> InboundPort <- [ domain ] -> OutboundPort <- DrivenAdapter

note: of course the ports are part of the domain

A concrete example of this could be:

WebController -> OrderServicePort [ order domain ] -> OrderRepositoryPort <- MongoDbOrderRepositoryAdapter

So the idea behind Ports and Adapters Architecture is the you test your domain separately from the concrete adapters at the edges of the application.

What I don't get is how exposing an interface Port on the left side is useful for testability?

WebController has no significant logic so would not be a test target per se and would be tested as part of end to end testing. So I would not mock the OrderServicePort as it normally would be my test subject.

Of course there is value in InboundPorts in that they only expose a simplified view of the concrete domain class that implements it.

But I can't see where the increased testability comes from by using InboundPorts.

Agree?

jakstack
  • 1,926
  • 3
  • 18
  • 31

2 Answers2

1

TL;DR;

Yes, I agree.


I see ports as an abstract concept, not tied to a language construct. I mean that a port doesn't mean an interface, and maybe doesn't even need an interface. In some languages there aren't even interfaces, so the ports need to be done using other language constructs.

I also see the primary/driving (inbound) ports and the secondary/driven (outbound) ports as artifacts with different concrete goals, although sharing the same idea of being an entry/exit point from the application core.

The way i see it, the secondary ports are a "prototype" of a tool the core needs, in the way it needs it. In many contexts this "prototype" is just an interface, but it is limiting to always think of it as an interface. The secondary adapter will be the concrete implementation of that port, wrapping around a library. This is powerful cozz it makes it easy to swap the library used, be it for testing or convenience.

However, the primary port its an entry point to the application, it starts a use case, it tells the application what to do, and how. Its a concrete thing, not a prototype. It IS the application core. So if we replace it, we are replacing the application core itself. A primary port is not a prototype like the secondary port, therefore, in itself, it doesn't need something like an interface.

The only reason to replace it, would be to test the primary adapters themselves, just to make sure they point to the correct use case. In such case, we would need something like an interface. But in such case we need to boot up the application anyway, which is usually the most expensive part or the test, so we might as well do it as an e2e, functional or integration test, where we also test the core, or part of it.

In some languages, or applications, or contexts, in some edge cases, maybe this needs to be done, maybe its the best way. We should not say something dogmatic like "never do ..." or "always do ...", it always depends on the context.

However, generally speaking, I agree with you: An interface on a primary port does not bring much advantage for testing, and i would go further and say that a primary port does not need an interface.

Wai Ha Lee
  • 7,664
  • 52
  • 54
  • 80
hgraca
  • 161
  • 4
0

I don't agree at all.

Driver ports are very important in hexagonal architecture testability, they are a key concept.

Driver ports are the API of the "hexagon" ("hexagon" = "business logic" = "application"). They are the use case (transacional) boundary, the left edges of the hexagon.

When you test the hexagon in isolation you run tests (driver adapters) against those ports, and you "mock" the driven ports.

How would you test the hexagon behaviour (functionality) if you don't have an API of that behaviour? You test the API (driver ports). They are the contracts of the SUT behaviour.

Even more, they let you detect business logic lekeage when you run the tests as regression tests.

Another useful thing is that they let you implement a hardcoded hexagon.

See this:

https://jmgarridopaz.github.io/content/hexagonalarchitecture.html#tc6-1-1

choquero70
  • 3,332
  • 2
  • 24
  • 40
  • Many thanks, in my question I mentioned that InboundPorts offer a "simplified view" that is indeed the use-case api that you refer to but my point is that they don't have to be interfaces to improve testability. The DrivingAdapters don't have to depend on InboundPort interfaces to improve testability, the InboundPorts can be use-case centric concrete classes or abstract classes without sacrificing testability. – jakstack May 01 '20 at 13:05
  • With interfaces you abstract away the implementation, it provides implementation hiding. I think that relying on interfaces improve testability in general because you don't depend on concrete classes, and you have a configurable dependency. You can hardcode the hexagon for example, and test the drivers in isolation. Regarding abstract classes, they are similar to interfaces, I would move one step further and do an interface with the abstract methods. The goal is to test hexagon behaviour, and the best way I know for it is driver ports as interfaces – choquero70 May 02 '20 at 12:50
  • thank you, two reasons I would use interfaces (1) to hide a sub-system and mock it when I'm testing the SUT (2) to simplify the view of a sub-system geared towards a narrow purpose like a use-case . For InboundPorts (2) is more applicable then (1) as I would not mock an InboundPort. Developers I've worked with repeat "it's better to implement to interfaces" and mechanically use interfaces needlessly ending up with naming like SomeServiceImpl which i hate because it indicates needless use of interfaces. Cheers – jakstack May 02 '20 at 15:13
  • "use interfaces needlessly ending up with naming like SomeServiceImpl"... this is not the case of hex arch at all, where naming ports is very important, since the port name should express the port purpose. Alistair Cockburn suggests naming a port "for doing something" ( see https://jmgarridopaz.github.io/content/hexagonalarchitecture-ig/chapter1.html#tc3-2 ). Also an interface allows encapsulation. Regarding (1) hardcoding the hexagon is also useful at early stage of development, for developing a test adapter – choquero70 May 02 '20 at 22:16