Showing posts with label IT architecture methodology. Show all posts
Showing posts with label IT architecture methodology. Show all posts

Sunday, 10 July 2022

CQRS is not a general purpose style of architecture

CQRS is not a top-level architecture pattern. Use it for components under stress (or if you need to expose a wide range of exposition models), but don’t make it a by default pattern for your whole platform / Information System

My first CQRS experience

The first time I built a CQRS system… it was in 2006. The term hadn’t be coined yet by Greg (that happened in 2010), but the practice was ubiquitous in finance with tons of high throughput platforms to build or to maintain.


Actually, the first time I’ve used this architecture style was not due to my young skills of tech architect, but rather on a technical constraint. The technology we were using to publish prices and other market data to our consumers (read side) was not really made to receive more than just subscriptions requests from end users. The throughputs and scalability capabilities we could have with that tech were great, but it wasn’t made to gather commands from consumers.


Hence we had to use another tech/channel in order to receive configuration change commands for our system (a pricing service). And because we had scalability and affinity constraints, I’ve ended up to split responsibilities of our nodes dealing with commands (a specific write cluster) from other ones dealing with queries/subscriptions computing the prices on demand and pushing them and following updates to our consumers (the read cluster). 

Without even knowing it, it was my first Command  & Query Responsibility Segregation experience

But the young technical architect that I was at the time learned a few things along the way from that experience.


Some observations over the years

  • Since then, whenever I need to scale and deliver great throughput figures, I always consider CQRS.
  • Every time I need to provide a wide range of exposition models for different kind of consumers, I also consider CQRS. 
  • Last but not least. When I’m doing Event Sourcing, I usually end up with CQRS too. 


Did I told you that I wasn’t the kind of people picking CQRS as default style when they say they want to do Domain Driven Design (DDD)? 

Even if it is hard, I’m trying to avoid Cargo-cult as much as possible for years, forcing myself to explain why I choose this pattern or that technology. 

I’m really against golden hammers in general.

And try to avoid embracing this mindset as much as possible. 

Year after year, I also regularly observed that CQRS was conflated with event sourcing for lots of people. It was the main reason why I wanted to make this talk in 2016 to the MS experiences days in Paris: « CQRS without event sourcing.» Slides:  https://www.slideshare.net/ThomasPierrain/cqrs-without-event-sourcing



During this session I’ve made a few recommendations, including this one: 

« CQRS is not a top-level architecture pattern. Use it for components under stress, but don’t make it a by default pattern for your whole platform / Information System »

I think that the first time I heard this take was from twitter Greg YOUNG. But it did vibrate with the strong and opinionated « there is no silver bullet » part in me. 



Retrospectively, I realize that I didn’t really get why it could be a bad idea at that time. 

And then a few years after, I also realized that publishing too many public/integration events was also a big mistake. 


Even with events, encapsulation is key

Indeed, you end-up violating the encapsulation of your app/API/Service. One should really consider every publicly published event (i.e. integration events between apps/API/Service) as tiny contracts. 
Once published, you will hardly get the chance to change them afterwards (due to the pressure of your consumers). 


Yet another mistake ;-)

And we weren’t the only ones making this mistake at first. 

Indeed, this has been a major warning communicated by the Event Driven Architecture (EDA) or #DDDesign communities back in the days

Publishing too many kind of integration events is the best way to end-up with a distributed monolith. And trust me, you will regret it. Very much.


When doing CQRS badly, there is a risk to publicly expose too many of your events outside of your own app/service/api boundary

One should avoid this at all cost.




The problem with passion

Recently, I’ve discussed with people that were so passionate about CQRS that they crossed that red line without even realizing it. 


Instead of them building specific read-side models and APIs for their external consumers (i.e. belonging to other Bounded Contexts-BCs) from their CQRS app, they wanted to publicly share a massive part of their CQRS private models & events with other teams and BCs. 


A kind of CQRS “à la carte” allowing others to build their own stuff from other's events, but across plenty of teams. A kind of “my CQRS is your CQRS”. Like if CQRS was a top-level architecture pattern…





Their idea was to let external consumers (to their app/service/api) build their own read projections from our events. To do so, they needed to publish a wide spectrum of their event types as public/integration ones on a common message broker so that other teams could clone all their relevant data within the events.

People thinking that “because our coupling is made with events makes it less painful” make a mistake.
One can easily spread the distributed monolith syndrome without even realizing it. 

The pain of not being able to refactor & align your code with your domain because you have leaked your initial naive vision towards too many consumers a daily source of frustration.

Encapsulation in OOP was not chosen by chance. The same applies at the cross-service architectural level. This is also why I have always appreciated the service-oriented approach (now API): it allows you to expose only what others need. No more no less.


When I understood what they had in mind and tried to achieve, I realized that the warning made by Greg and other was far beyond the message I initially got of “there is no silver bullet” or “you don’t have to systematically pay this accidental complexity for everything”. 


The reasons behind are far more important and the risks higher. If you don’t want to build a distributed monolith please read carefully this warning: 

« CQRS is not a top-level architecture pattern. Use it for components under stress (or if you need to expose a wide range of exposition models), but don’t make it a by default pattern for your whole platform / Information System »




And don’t let others build their own read projections from your own internal implementation details.

This article was extracted from a twitter thread available here (with all the answers and comments too): https://twitter.com/tpierrain/status/1546039271676678144?s=21&t=trjFLLZ35oXsxpxXeW7XCw


Sunday, 29 November 2020

Hexagonal or not Hexagonal?

TL; DR: hexagonal architecture is a fabulous pattern that has 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 compares 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 took back this pattern out from its ashes ;-) and made it the new way to be explored deeper. This community was gathering people who 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 couldn’t this dev team 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 minute, 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 brings 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 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 was 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 stuff

As I already 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 a sweet spot 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, done 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 architecture 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 stuff 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 heuristics 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's (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 infrastructure or someone else external concepts that should not bother my domain logic. 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's 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 it 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 know 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 has 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. 


 

Sunday, 29 March 2020

Hexagonal architecture: don't get lost on your right-side

Fan of hexagonal architecture and its promises for a very long time, I often spent time to understand it, explain it but also to demystify its implementation. On the other hand, I've often limited my explanations to the simple cases. What we call the “happy path” where everything goes well. In this first article of an upcoming miniseries, I want to talk about some implementation details as well as traps in which it is very easy to fall. Finally, I will take some time to talk about how to test your hexagon, suggesting a non-orthodox strategy. The idea being to save you a little time during your implementations of this incredibly useful architectural pattern.

The "non-cohesive right-side adapter" anti-pattern

We will be starting today with a design-smell: the non-cohesive right-side adapter. If you do not know hexagonal architecture yet (a.k.a.: ports and adapters), I suggest you start by reading my previous post on the subject which summarizes all this. Because to explain the non-cohesive right adapter anti-pattern, we are just going to recall here a few fundamentals about Alistair COCKBURN’s hexagonal architecture:
  • We split our software in 2 distinct regions: 
    • The inside (for the Domain / business code that we call "the Hexagon")
    • The outside (for the Infrastructure / tech code)
  • To enter the Hexagon and interact with the domain, we talk to a port that we call "driver port" or "left-side port"
  • The domain code will then use one or more third-party systems (data stores, web APIs, etc) to gather information or trigger side-effects. To do so, the business code must remain at its business-level semantic. It is done by using one or more "driven ports" or "right-side ports".
  • A port is therefore an interface belonging to the Domain using business semantics to express either requests that we address to our system (left-side port) or external interactions that it performs along the way to achieve its goal (right-side port). I like to see ports as drawbridges we are using to come and go between the infrastructure side and the Domain one (analogy that Cyrille MARTRAIRE had whispered to me a very long time ago)
  • In terms of dependency, the infrastructure code knows and references the Domain one. But let's be more precise here (BTW, thank you for your feedback Alistair ;-): only the left-side infrastructure code knows the Domain actually (to interact with it through a left-side port).
  • On the other hand, the Domain (business) code must never reference any technical/infrastructure-level code. It works at runtime thanks to the Dependency Inversion principle, that Gerard MESZAROS called once "configurable dependencies" (which lends itself very well to Alistair's pattern)
  • An adapter is a piece of code which makes it possible to pass from one world to another (infra => domain or domain => infra) and which takes care in particular of converting the data structures of a world ( ex: JSON or DTO) to the other (ex: POJO or POCO of the domain)
  • There are 2 kind of adapters:
    • "Left-side adapters" which use a left-side port to interact with the Domain (thus having an aggregation-type relationship with it)
    • "Right-side adapters" which implement a right-side port instance

The problem with the right wing...

You may have noticed here that I did not say that a right-side adapter implements multiple right-side ports. It was intentional.
Because if you did, there is a good chance that you will land on the anti-pattern that I want to mention here: the non-cohesive right adapter.
This is what happens when you give too much responsibility to a right-side adapter: you risk then deporting part of the Domain logic of your hexagon into one of its peripheral elements on the infrastructure side (i.e. the adapter). This is what happens especially when an adapter references and uses another adapter directly.
Of course, this is something I strongly advise against doing. Indeed, you need to keep all the business orchestration logic at the Domain level and not to distribute it here and there at the infrastructure code level. But since I've seen that everyone at least falls into this trap once, it's worth mentioning. Avoid this situation if you don't want to end up with an anemic Domain.
(click on the image to zoom)
 (click on the image to zoom)

Worst. In that case, those who test only the center of the hexagon (i.e. the domain code) by stubbing the adapters all around would miss out on lots of code and potential bugs. Those would be tucked away in your untested adapters.
It is indeed extremely easy to deceive yourself by putting a lot of implicit in the behaviours of your adapter stubs. You will very often put in those stubs what you think the adapters will do or should do. But the risk afterwards is to fail to implement the concrete adapter properly (when you will forget all the implicit you initially put in your stubs in the first place). This will give you a false confidence with a fully green test harness, but potentially buggy behaviours in production (when using the real concrete right-side adapters).

Let's talk a little bit about test strategy

I have already written and spoken on the issue but since I have not yet translated into English my most specific article on the subject, I will just clarify here some essential terms to understand the rest of the discussion.
After more than 15 years of practicing TDD at work, I have arrived over the years to a thrifty form of outside-in TDD which suits 90% of my projects and contexts very well. Outside-in means that I consider my system (often a web API) as a whole and from the outside. A bit like a big black box that I’m going to grow and improve gradually by writing acceptance tests that will drive its behaviours and reliefs (see the fantastic GOOS book from Nat Pryce and Steve Freeman if you want to know more). Those acceptance tests aren’t interested in implementation details (i.e. how the inside of the black-box is coded) but just on how to interact with the box in order to have the expected reactions and behaviours. This lets me easily change the internal implementation of my black box whenever I want without slowing me down or breaking fragile tests.
My acceptance tests are therefore coarse grained tests covering the full box (here, the entire hexagon) but not using real technical protocols neither real data stores in order to be the fastest possible (I'm addicted to short feedbacks and thus use a tool to run my tests every time I change a line of code).
Besides these acceptance tests which constitute 80% of my strategy, I generally write some fine-grained unit tests (the small tests of the "double loop" when I need them), some integration tests, and some contract tests for my stubs to check that they behaves like the real external dependencies they substitute with (and also to detect when something change externally). The rest of this article will mainly talk about my coarse-grained acceptance tests.

The best possible trade-off

This parenthesis about testing terminology and my default test strategy being closed, let's focus on our initial problem: how to avoid writing incomplete tests allowing bugs to happen at runtime when we deploy our system on our server.
My recommendation: write acceptance tests that cover your entire hexagon, except for the I/O at the border. Yes, you heard me right: I advise you to write acceptance tests that use all your concrete adapters and not stubs for them. The only thing you will have to stub are network calls, disk or database access which are made by your adapters. By doing so, you test all your code in full assembly mode (without illusions or unpleasant surprises in production therefore), but blazing fast ;-)

 (click on the image to zoom)
(click on the image to zoom)

 

Can you give me an Example?

Sure. Let's take an Example to make the implicit, explicit.
Imagine that you have to code a sitemap.xml file generator for the SEO of a web platform that has a completely dynamic content controlled by several business lines (e-biz, marketing). This hexagonal micro-service could be a web API called daily in order to regenerate all sitemaps of the related website. You call a HTTP POST request, and all the sitemap files are then updated and published. (as a side-note: Please, don't tell my mum that I mentioned microservices in one of my articles ;-)
Okay. Well instead of writing acceptance tests like this where every adapter is stubbed:
  (click on the image to zoom the code)

I rather advise you to write acceptance tests in which you will include your concrete adapters and only stub their last-mile I/O. It is worth mentioning here that I’m using my ASP.NET web controller as my left-side adapter. Anyway, it looks like this: 
(click on the image to zoom the code)

There is a better way

Of course, in order to have readable, concise (no more than 10/15 lines long) and expressive acceptance tests (i.e. by seeing clearly the values ​​transmitted to your stubs), you will generally go through builders for your stubs, mocks. You can also use them to build your hexagon (which is a 3 steps initialization process). I didn't show it earlier because it would have been less clear for my example but a target version might look like this:
 (click on the image to zoom the code)

A good adapter is a pretty dumb adapter

Be careful. Testing your full hexagon with its concrete adapters should not prevent you from putting all your business logic or orchestration at the heart of your Domain code (i.e. the heart of the hexagon).
A good adapter is a pretty dumb adapter. As a reminder, its role is to translate a model linked to infrastructure to a business model and vice versa + to make the link with the necessary technology. Above all, you should not have a situation where an adapter uses another adapter to play tricks or optimize certain calls.
When you are doing Domain Driven Design, you aim to reduce as much as possible the accidental complexity of your system (i.e. the complexity related to your tools, idioms or programming language) while trying to focus only on essential complexity (i.e. the complexity related to the business problem to be solved). In that context, the infrastructure code of your hexagon should be a pass-through. No more, no less (note: we won’t talk about Anti-corruption Layers here. That will be worth another article).

Expected objections

People generally agree on the need to keep the domain in the center of the Hexagon. On the other hand, I've got some objections related to my testing strategy. Let's review some of them.


First, it could be objected here that my acceptance tests are a bit hybrid and that they cover both Domain considerations but also technical ones. Not very orthodox...
Well... actually I don't realy care about orthodoxy or by-the-book-ism ;-) As soon as you can explain your choices and the trade-offs you, I'm ok with that. Moreover it can be completely transparent if you make sure that each of your acceptance tests covers business behaviours of your system (service / API / microservice...). It's up to you to work on the expressiveness of your tests: their names, their simplicity, their ubiquitous language, but also the conciseness of the "arrange" steps (using DSL-like expressivity for example).
I've landed on this new strategy this year after tons of trials and errors. Before that I was implementing the very same test strategy often put forward by the London friends from Cucumber (Seb Rose, Steve Tooke and Aslak Hellesøy). That one aims to combine:
  • A lot of acceptance tests but for which we stub all the adapters (thus blazing fast tests)
  • And some additional integration/contract tests aside, in another project. These contract tests verify that our adapter stubs used in our acceptance tests have exactly the same behaviour as their real concrete adapters (the one that we package and deliver with our hexagon in production). These last integration tests are therefore much slower and run more rarely (mainly on the dev factory, not within my local NCrunch automatic runner). But no surprise here: cause it's an expected trade-off

So, we have had a really interesting situation here, which allowed us to have tons of super-fast acceptance tests AND to be confident enough about our right-side adapter stubs to be true to reality enough (for some reference scenarios).
I talk about it in the past-tense here because I haven't really managed to be confident enough with this setup during my various experiences with different teams.

Blind spot

Indeed, I regularly had problems with this strategy because we didn't cover enough cases or errors or exceptions within those contract tests of our right-side adapters. Too many happy paths (basic scenarios where everything goes well), not enough corner cases and exceptions in those contract tests. To put it another way, our acceptance tests were asking our stubs on many more cases than what was planned in their contract tests.
We developers are attracted to the happy path as much as moths are to a lit bulb. It seems to be one of our determinism (quite the opposite of QA people ;-) I knew that already. But I observed these situations carefully to try to understand what had made us fail here (in my various teams and contexts). I came to the conclusion that it was because these integration tests - in addition to being very "plumbing oriented" (more legacy-oriented than domain-oriented) - were much slower to run than our unit or acceptance ones. Every time you add a new parameter combination, it increases the overall test harness execution latency. That’s why my people paid less attention to it. A little bit in mode: "anyway, it's an integration test for a stub... it should do the job but we're not going to spend too much time and effort on it either".
As a consequence, we used to test a lot less case combinations within these contract tests for our stubs than what we used to do in our other tests (i.e. coarse-grained acceptance tests or fine-grained unit tests).

Stubbing less

The side-effect of this was that combination of contract tests and acceptance tests was not sufficient, to manage to catch all the bugs or plumbing problems in our final assembly. It is therefore for these reasons that I finally arrived at the test strategy that I presented to you in this article, and which includes the concrete adapters in our acceptance tests. On the other hand, I continue to use this strategy of testing contracts for external components or third-party APIs. But now I stub less things, my stubs only cover a very fine part of my system.

One can Pick hexagonal architecture for different reasons

This one is important. As a final warning, I should also point out that my testing strategy has been designed and worked well in my contexts so far. Of course, I’m not saying here that this is a one size fits all situation (I don’t even think that such a situation exists).
In particular, my contexts aren’t the one faced by Alistair when he created the pattern. Back in the days, Alistair had to find a solution in order to survive with a huge number of connectivity and technologies, to avoid his Domain logic to suffer from a combinatorial explosion (for a weather forecast system).
At work, I mainly use hexagonal architecture pattern because it allows me to split my business code from my technical one. Not to combine or easily switch my left-side technologies and adapters. Reason why I usually have a unique exposure for my business services which fits exactly my purpose and context: REST-like HTTP for a web API, Graph QL sometimes for website backends, Aeron middleware for a low latency service, AMQP-based middleware for some HA, resilient and scalable services, RPC for... (no, I'm kidding, I hate so much the RPC paradigm ;-)
More than that, it is very likely that the communication middleware I picked has more impacts on my business code interactions (left-side port) than a simple switch of Adapters. In some cases, the middleware technology I use may even impact my programming paradigm (event-driven reactive programming/Classical OOP/FP/lame OOP/transaction script). Reason why my left-side port may often become a leaky abstraction.
As a consequence, I only need one adapter on the left side. This saves me from having to run all my acceptance tests as many times as I would have different left-side exposure technologies. That is worth mentioning (at least for people that would have a similar objective that Alistair had).
And when my goal is to build a REST-like web APIs, I even use my web controllers as left-side adapters instead of creating another ad-hoc adapter type (this is something we are legion to do, if I recall Everyone who chatted with me during conferences or my DDD training sessions).

An interesting side-effect

The technique I was promoting here to test everything but not the I/O will put you in a very comfortable situation at the end of the day. Indeed:
  • your acceptance tests will cover a base code very faithful to the reality of the production (my main driver here)
  • your acceptance test harness will allow you to calmly refactor your code base in the event that you have made a mistake and put some business behaviour into your adapters. Thanks to this test harness covering everything, the move of the wrongly located business code from the right-side Adapter to the Domain can be done without any risk.

Perhaps we could find an easy way (other than pairing or code review) to prevent less experienced people from falling into this trap. For the moment, this ability to easily refactor -a posteriori- when we screwed this (or when we took a little short-term technical debt), was more than enough for my projects.

Conclusion

Since this post is long enough, I’ll be brief:
  1. Always go through the center of the hexagon, do not connect your right-side adapters to each other
  2. Do not code YOUR domain logic or YOUR domain orchestration logic in your adapters
  3. Test your entire hexagon (including adapters). To do so, only stub your last miles I/O from your right-side adapters.
  4. Aside, keep testing your test doubles or stubs of external dependencies against real implementations via contract-based integration tests.

The next 2 articles in this series dedicated to hexagonal architecture will talk about the subject of health checks (how to know if our hexagon is in shape or not) but also about the comparison with an alternative to the pattern: Functional Core (with imperative shell). 
Happy Coding! See you soon.
Thomas