Ports and Adapters Architecture
herbertograca.com
herbertograca.com
A pretty big chunk of the code that annoys me most at work is a result of this kind of premature abstraction ("maybe we'll need to swap out the database layer one day"). As far as I can tell most of the time this is never needed, and the net time lost by hundreds of developers over several years is much greater than the cost of forgoing the extra layers and just making the transition the hard way as and when it's required.
Without the proper abstractions things can become tightly coupled, and your going to spend a lot of time untangling it.
I've been on projects like this, and spent a lot of time getting annoyed at the lack of foresight.
The main issues I faced in this respect are EOLing (specifically, by license change, or incompatible technical change - e.g. A 16 bit lib unavailable for 32 bit or a 32 bit lib unavailable for 64)
How much would you have come across if most applications implemented this pattern, though? There's some selection bias at play.
Or to look at it from the other side: how many times have you come across a switch when this pattern was implicitly implemented in the application by virtue of using a standard protocol, like SMTP?
Layers are a lot easier to comprehend than hexagons. That makes other tasks easier, like planning deployment of layers into tiers, through which routes are similarly linear.
Think of P&A as terminology to help you disambiguate these use cases from the extremely overloaded words. If your team know these terms, it makes a lot of discussions a lot clearer.
The hexagon thing is a bit of a red herring IMO. I think of it as dependency inversion at the module level, rather than the class level.
On the other hand, "port" is already completely overloaded: serial interface, TCP socket endpoint, etc. Further overloading it - particularly when there is a widely used term for the same thing - makes no sense.
Reinventing terms for existing concepts does nothing - in my mind - to advance the state of art.
class FooService : IFooService{} interface IFooService {}
where there is an interface with a single implementation. In this case, the "service interface" isn't a port, because it's not actually decoupled from the domain.
It has the potential to be one, but only if the FooService is extracted, dereferenced from the domain, and the application composes the FooService.
Saying "we need to make this into a port and adapter" gets the point across very quickly.