Sunday, 29 November 2020

Hexagonal or not Hexagonal?

TL; DR: hexagonal architecture is a fabulous pattern that have more advantages than the ones for which it has been originally created. One can think in an orthodox vision that patterns do not evolve. That it is important to keep Alistair Cockburn’s pattern like it was described back in the days. One can think that some patterns may evolve, that Hexagonal Architecture has more facets than we think. This article discusses and compare these 2 approaches, takes an example with one new facet related to DDD, before asking an important question to its creator and the dev community at large (at least important for the author of this post ;-)

 

First Adapters were technological

Hexagonal architecture has been initiated during a project that Alistair Cockburn had done once related to a weather forecast system. His main objective seemed to support a great number of different technologies to receive weather conditions as inputs but also to connect and publish the results of their weather forecast towards to a huge number of external systems.

This explains why he found the concept of interchangeable plugins (now we say ‘configurable dependencies’, following Gerard Meszaros) for technologies at the borders of the app, and a stable application-business code inside (what Alistair called the “hexagon”).

The pattern had some traction by the time (see the amazing GOOS book: http://www.growing-object-oriented-software.com talking about it for instance) but it is after more than a decade after that a community take back this pattern out from its ashes ;-) and made it the new way to be explored deeper. This community was gathering people wanted to focus and foster more on the business value of our software (rather than pure tech fads): the Domain Driven Design (DDD) community.

 

Then came the DDD practitioners

As a member of this DDD community, I found the pattern very interesting for many other reasons. But the main one being the capability to protect and isolate my business domain code from the technical stacks.

Why should I protect my domain code from the outside world?!?

I still remember that day in 2008 when I witnessed a bad situation where a major banking app had to be fully rewritten after we have collectively decided to ban a dangerous low latency multicast messaging system (I was working for that bank at the time). We had taken that decision because we were all suffering from serious and regular network outages due to multicast nack storms. We were in a bad and fragile situation where any slow consumer could break the whole network infrastructure shared by many other applications and critical systems of the bank. Sad panda.

Why do this dev team couldn’t just switch from one middleware tech to another? Because their whole domain model was melted and built with that middleware data format at its core (i.e. instances of XMessages everywhere in their core domain ­čś│).

Moreover, the entire threading model of this application was built upon the one from the low latency middleware library. In other words: the need of being thread-safe or not in their code was depending on the premises and the things that were guaranteed so far by the middleware lib. Once you removed that lib, you suddenly lost all these thread-safe guarantees and premises. The whole threading model of this complicated application would have vanished, turned the app unusable with tons of new race conditions, Heinsenbugs and other deadlocks. When it’s an app that can lose Millions of Euros per minutes, you don’t play this decision at head or tail ;-)

I’m rarely proponent of rebuilding from scratch and often prefer the refactoring of an existing code that already bring value. But in that case where everything was so entangled... it was really the best thing to do. But I let you imagine how selling this kind of full reconstruction to the business was challenging...

Anyway. I wanted to tell you this story in order to emphasize that splitting your domain code from the technology is a real need. It has real implications and benefits for concrete apps. It’s not it is not a simple architect coquetry. I’m not even talking about the interesting capability to switch from one techno to another in a snap as needed by Alister. No. Just a proper split between our domain code and the infra-tech one.

It’s crucial. But it’s just the beginning of this journey.


Models Adapters

Indeed. With his pattern, Alistair were trying to protect and keep his application domain code stable in front of the challenge of having a huge number of different technologies all around (reason why I talked about Technologist Adapters earlier). But splitting and preserving our domain code from the infrastructure one may be not enough.

As DDD practitioners, we may want to protect our domain code from more than that.

As DDD practitioners we may also want to protect it from being polluted by other models and external perspectives (one can call them other Bounded Contexts).

>>>>> The sub part in grey below explains the basics of DDD you need to understand the end of the post. You can skip it if you already know what are Bounded Contexts (BCs) and Anti-corruption Layers (ACLs).


Bounded Contexts?

A Bounded Context (BC) is a linguistic and conceptual boundary where words, people and models that apply are consistent and used to solve specific Domain problems. Specific problems of business people who speak the same language and share the same concerns (e.g. the Billing context, the Pre-sales context...)

DDD recommends for every BC to have its own models, taylor-made for its own specific usages. For instance, a ‘Customer’ in a Pre-sales BC will only have information like Age, socio-professional categories, hours of availability and a list of products and services we already bought etc. Whereas a ‘Customer’ in the Accounting-Billing BC will have more specific information such as Address, Payment method etc. DDD advises us to prevent from having one ‘Customer’ model only for every BC in order to avoid conflicts, misinterpretation of words and to allow polysemy between 2 different BCs without incidents.

The interesting thing with the Bounded Context of DDD is that they can be designed autonomously comparing to the other ones (reason why some people are saying that DDD is a way to do Agile architecture). 

DDD also comes with lots of strategic patterns in order to deal with BCs relationships. One of them being the famous Anti-Corruption Layer (aka. ACL).

Anti-corruption Layer FTW

As I already explained in another post, the Anti-Corruption Layer is a pattern popularized by the DDD which allows a (Bounded) Context not to find itself polluted by the inconsistency or the frequent changes of a model coming from another Context with which it must work.

>>>>> 

 

 

An ACL is like a shock absorber between 2 Contexts. We use it to reduce the coupling with another Context in our code. By doing so, we isolate our coupling in a small and well protected box (with limited access): the ACL.

Do you see where I am going with this?

ACL is a kind of Adapter. But an Adapter for Models, not purely technological stuffs

As Ialready wrote elsewhere, an anti-corruption layer can be implemented in various forms.
At least 3:

  • external gateway/service/intermediate API
  • dedicated in-middle database (for old systems)
  • or just an in-proc adapter within a hexagonal architecture

And this is the last version that will interest us for the rest of this post.

Indeed, the [technological] adapters of Alistair’s first description of his pattern may be sweet spots for the [models] adapters we need too when we focus on languages and various models (like when we practice DDD).

And if we do agree on the occasional need of having Models Adapters (aka. ACL) in our Hexagonal Architectures, we can start discussing the options and the design tradeoffs

 

ACLs in Hexagonal Architecture, OK. But where? 

The next question we may ask ourselves is: where to put our ACL code when we want to have it within our Hexagonal Architecture? (I.e. the last choice of the three presented above). There have been debates about it on Twitter recently. And the main question was:

Should we put the ACL outside or inside the hexagon?

As an important disclaimer, I would say that there is no silver bullet nor unique answer to that question. As always with software architecture, the design choices and the tradeoffs we make should be driven by our context, our requirements and set of constraints (either technical, business, economical, sourcing, cultural, socio-technical...).

That being said, let’s compare these 2 options.


Option 1: ACL as part of the Hexagon

Spoiler alert: I’m not a big fan of it. To be honest, been there, did that, suffered a little bit with extra mapping layers (new spots for bugs). So not for me anymore. But since it has recently been discussed on twitter, I think it’s important to present this configuration.

 

This is the ‘technological’ or the orthodox option if I dare. The one saying that driven Ports and Adapters on the right-side should only expose what is available outside as external dependencies. And to do it without trying to hide the number nor the complexity of what it takes to talk or to orchestrate with all these external elements.

We usually pick that option if we consider that coping with other teams’ structural architectures is part of our application or domain code. Not the technical details of them of course. But their existence (i.e. how many counterparts, APIs, DBs or messaging systems do we need to interact with).

And for that, we necessarily need a counterpart model in our hexagon FOR EVERY ONE OF THEM!

Why? Remember, we don’t want our hexagon to be polluted by external technical DTOs or any other serialization formats. So, for every one of them, there will be an adaption (in the proper driven Adapter) in order to map it with its non-technical-version. The one we need for our hexagonal code to deal with it. An Hexagonal-compliant model (represented in blue in my sketch above).

It’s important here to visualize that our ‘Hexagonal code’ in this option, is composed by the Domain code + the ACL one (but an ACL that won’t have to deal with technical formats). 

Why I abandoned this option over the years with my projects

To conclude with that first option, I would say that there are 2 main reasons why I abandoned it in lots of contexts:

  1. It forces us to create 1 non-technical-intermediate model (in blue) for every external dependency (leading to 5 different models in our example). This is cumbersome, and bug-prone. I saw lots of devs being tired of all those extra layers for a very limited benefit (i.e. just to follow Hexagonal Architecture by the book)

  2. It opens the door for junior devs or newcomers to introduce technical stuffs within our hexagon. “But... I thought it was ok since we already have an ACL in that module/assembly?!?” It reduces the clarity of the domain-infra code duo.

These are the reasons why I progressively moved over the years towards another tradeoff. A new option which is one of my favorite heuristic now.

 

Option 2: ACL within a driven Adapter

This option consists of putting our ACL code into one or more adapters.

If we think that it makes sense to replace 2 different Adapters into one ACL Adapter doing the orchestration and the adaptation, we can even avoid coding the intermediate layers we previously had for every Adapter (in blue on the option 1 diagram). It means less plumbering code, less mapping and less bugs.


When something changes in one of the external backends used by the ACL Adapter (let’s say a pink square), the impact is even reduced comparing to the Option 1.

Indeed, all you have to change in that situation is your ACL code adapting this external backend concept to one of your domain code one (black circles on the diagrams).

With option 1, you will have more work. You will also have to change the corresponding intermediate data model in blue (with more risk of bugs in that extra mapping). 

 

Clarifications

As I am not an English speaker (one may have noticed ;-P I think it is worth clarifying several points before concluding:

  1. I’m not saying that one should always put ACL in our hexagonal architecture

  2. I’m saying that if you need to have an ACL in your hexagonal architecture, you should definitely use the sweet spot of the Adapters to do so.

  3. I’m saying that in some cases, you can even merge 2 former Hexagonal Adapters into a unique one that will play the ACL role

  4. I always want my ports to be designed and driven by my own Domain needs and expressivity. I don’t want my domain code to use. In other words: Putting a driven port for my domain concept, instead of putting a driven port for each external system is not a mistake. It’s an informed decision.



To conclude: hexagonal or not hexagonal?

When Alistair created his pattern, his main driver was to easily switch one technology with another without breaking his core domain code. Adaptability was his main driver and big variance of technologies was his challenge.

I call this the "technological facet" of the pattern, recently confirmed by Alistair on twitter:

“The Ports & Adapters pattern calls explicitly for a seam at a technology-semantic boundary” (https://twitter.com/totheralistair/status/1333088400459632640?s=21)

But one of the keys to the pattern success in my opinion was Alistair's lack of detail in his original article. We all saw value in it, but almost everyone struggled to understand it. Almost all of us have had our own definition of what a port is and what an adapter is for years (you can open some books if you want to check that out ;-)

This fuzziness allowed some of us to play with it, freely to discover new facets or properties from it.

One may discover that Ports and Adapters were top notch for testing (the "testability facet"). Another one may discover that Ports and Adapters were truly awesome to properly split our domain code from the infrastructure one (the "tactical DDD facet"). Another one may find out that it may help to reduce layering and complexity of our architecture (the "simplicity facet"). Another one may realize that the initial premise of the Pattern was rarely the reason why people were using it. That the ports were often leaky abstractions preventing you to properly cover multiple technologies behind (the devil is in the detail). One may find it interesting for having a good time to market and quick feedbacks about what is at stakes (the "quick feedback facet"). One may find it intersting to postpone architectural decisions at the right moment (the "late architectural decisions facet"). One may find interesting to Adapt more than technologies. To adapt not only technologies but also external models, like I described here (the "strategic DDD facet").

Even Alistair evolved and changed his mind over the years about important things such as the symmetry or the absence of symmetry of his Pattern (now we all knows that the left and right side are asymmetrical ;-)

A multi facets pattern?

I personally think that the beauty of this pattern stands on our various interpretations and implementations.The fuzziness of the original Hexagonal Architecture article from Alistair also have something in common with Eric Evan's Blue Book. It’s so conducive to various interpretations that it ages really well.

Like the image of the Hexagon itself, it’s a multi facets pattern. Maybe richer and more complex than Alistair realized it so far.

My intent here is to ask Alistair and every one of you in the DEV community: should we keep talking about Hexagonal Architecture and its multiple facets, or should we start finding new names for some of those facets and awesome properties

I’m more than keen to have your answers. 


 

Monday, 13 April 2020

Adapters are true Heralds of DDD

A few days ago, I posted an article to warn people about some pitfalls one should avoid when implementing an hexagonal architecture. One of these pitfalls is to leak part of our domain logic to one or more adapters (therefore on the infrastructure code side). Whereas I'm convinced that this is something to be avoided at all cost (in order for our Domain code to remain coherent and not entangled with technical issues), I would like here to linger a little and revalue an area that I have myself got into the habit of shouting slightly: the code outside the hexagon. Today, the passionate practitioner of Domain Driven Design that I am wants to assert that the right-side adapters of our hexagonal architecture deserve much better than the treatment usually reserved for them. What if this infrastructure code could ultimately be just as important? What if it could be the place of major challenges for our systems?



The origin of DDD

Domain Driven Design brings together many things. But originally,

DDD started from an attempt by Eric Evans to give more autonomy to the people and different teams 

who worked - sometimes with too much friction - in the same structure where he was also (finance industry).

To get there, Eric had the idea of ​​bringing out his key notion of Bounded Contexts. A Bounded Context is a linguistic and conceptual boundary where words, people and models that apply are consistent and used to solve specific Domain problems. Specific problems of business people who speak the same language and share the same concerns. We can therefore say that DDD militates for use-case-centric specific models, modelled on groups of people.

Of course, all this being very specific to an organization and the business processes set up in it. In the same company, we can find, for example, one or more Bounded Contexts related to "marketing", another to "accounting", another one specific to "delivery service", etc. When doing DDD, we generally try to identify and characterize these different Contexts to know where we are, whether it really brings business value or only relates to support functions. It will drive our effort and ways of working out.

In the process, we are also trying to identify the other Contexts with which we will have to collaborate. All this is usually illustrated in a diagram which represents Bounded Contexts (represented like big potatoes) and which one names Context Map (see the example below).

The entire strategic chapters of the DDD describes these relationships between Contexts (human and technical) and provides us with patterns to manage this or that situation. To connect and communicate between Context given the power relations involved.

Here is a naïve example of Context Map applied to a hotel group which here distributes its hotel rooms through its own distribution platform (web and apps).

Context Map from the hospitality industry


SOA FTW!

As we read a lot of "different" things on the subject ;-) I would like to give here a few ideas of how this concept of Bounded Context may relate to other more tangible software artefacts you already know. To make the implicit explicit (which is another mantra of DDD). Also, I will quite naturally talk about service oriented architecture here, since it is a style of architecture which I find particularly useful when we do it right (note: remove all this former soap things from your head ; -)

When practicing DDD, the size of a service does not matter, we are rather looking for its proper alignment with the business (and therefore within a Bounded Context).

This is why DDD may help a lot people to escape the micro-services quagmire (where people are only focusing on the size...)

Within the same Bounded Context, we will often find one or more services (web APIs in general nowadays). Since Alistair's pattern is very handy for implementing a service I will often use it to implement them.

The general case is to have one hexagon per process (which can be scaled out with multiple containers or VMs) but it is not mandatory.

In some cases, one can assembly multiple hexagons within the same process (In-proc). Every hexagon will interact with others using the same ports but with different Adapters in between that will only do in-proc calls (instead of network calls). We usually call this multiple-hexagon situation as Modular Monolith, which is a good thing actually (as opposed to old-school Monoliths or even worst: distributed Monoliths/Distributed big-ball-of-mud ;-)



One can have multiple hexagons or services in a Bounded Context.
Or even multiple hexagons in the same process (Modular Monolith)

A common DNA

Making autonomous models, splitting them by Context and preventing them from being entangled. Doesn't ring a bell to you?

No surprise Alistair’s pattern suits really well DDD practitioners. The ports and adapters effectively allow us to prevent DTOs of an external API (often coming from another context therefore) from parasitizing and entering our hexagon which is supposed to only manage problems from the Bounded Context to which it belongs.

Without that, coupling between those 2 different Contexts would be hellish because we would have some bits other contexts within our business code. Imagine now the impact on our code in case the other Context dev team constantly changes their API or DTO contracts without asking our opinion. To protect ourselves from such a situation, the DDD toolbox offers us an interesting pattern: The Anti-Corruption Layer (ACL).



Anti-Corruption what?!?

The Anti-Corruption Layer is a pattern popularized by the DDD which allows a (Bounded) Context not to find itself polluted by the inconsistency or the frequent changes of a model coming from another Context with which it must work.

It’s like a shock absorber between 2 Contexts. We use it to reduce the coupling with another Context. By doing so, we isolate our coupling in a small and well protected box (with limited access): the ACL.

Then, any untimely change of the external context will have very little impact on our business code which acts as a consumer of the ACL and no longer directly of the unstable code coming from the external context.


No ACL here, so the model of the other Bounded Context leaks everywhere within our hexagon


Here, our ACL Adapter prevents models of another Bounded Context from entering our hexagon


An anti-corruption layer can be implemented in various forms:
  • external gateway/service/intermediate API
  • dedicated in-middle database (for old systems)
  • or just an in-proc adapter within a hexagonal architecture.


Which Eurythmics to choose for your ACL?

Any good writing on DDD must now have a "Heuristics" moment ;-) Here is mine:

If you have several consumers originated from your Bounded Context (potentially several APIs or components) who need to interact like you with the same portion of the other Bounded Context outside, it is in your best interest to do emerge a shared facade service (API) that would act as an ACL.

If you only have one consumer for these interactions with another Bounded Contexts (i.e. your hexagonal domain code), you can just implement your needed ACL at the Adapter level of your Hexagonal Architecture.

It is very common to start by implementing an in-proc adapter here, and then to move out as a stand-alone API if the need arises.

An Anti-Corruption Layer can be hosted in an external dedicated process/API/gateway


An ACL often implies orchestration of various calls towards the same or different external systems in order for your domain code to have the behaviours or the answers it expects.

It is quite logical because none of those existing external Bounded Context systems was made specifically for you and your needs. They usually speak with their own language and do not directly answer questions you ask yourself in your own other Bounded Context. In most cases, the external systems you will need to operate with are rotten legacy systems, or a mix between an old system and a brand new API that only works on a limited number of cases.

The ACL-Adapter of your hexagonal architecture will therefore very often have to call these multiple sources to be able to offer a consolidated vision to your domain.


For instance:

  1. Calling a first service of another BC to recover incomplete data
  2. Translating or adapting part of its response into another data structure ready to be used to forge an intelligible request to another external service (often belonging to the same second BC but not always)
  3. Sending this second request to the second system or endpoint
  4. Getting the result of this second call and adapt the whole set to produce a response that will finally suit your own Hexagon with its Domain-level semantic

  Example of ACL-Adapter orchestrating different calls to different back-ends / APIs of another BC



My Domain is not your domain

In most cases, all this awareness of the other external Bounded Context Systems, the discrepancies of how to ask them questions has nothing to deal with your own Domain code. These implementation details must remain at your adapter level

In other cases, you will find important that the center of your hexagon explicitly goes to look for 2 different things/concepts via two different operations or two different right-side ports before doing some stuffs or even assembling them in order to become something useful for you.


There is no silver bullet

Do not expect me to give you a universal or absolute rule to answer this question. It's Design. And of course, it will depend on your context and your Domain.

The right question to be asked every time is: is it legitimate and useful for my domain code to be aware of these concepts or details?

If so, your hexagonal Domain code should explain these interactions and exchanges. If not (when we don't want to be polluted by the messy external BC for instance), this knowledge should rather remain at your right-side adapter level.


Mea culpa

In the light of these cases and explanations, you will understand why I really regret having written in my previous post that a "good adapter is a pretty stupid adapter". This is the case when there is a simple 1-to-1 relationship between your hexagon and another external system.

But in practice it is quite often the opposite (and even more so if your adapter is an ACL).

And therein lies the paradox. For years I have only been focusing on the Hexagonal Domain code. With practice and all the non-trivial cases encountered in my career, I realized that


These Adapters are much more than just Extras. 

As such, they also deserve much better than just being stubbed in our hexagonal architecture acceptance tests

But on that testing strategy topic, I will come back very shortly with a new episode of this series devoted to hexagonal architecture.

Stay tuned!