Adopting New API Versions With Hexagonal Architecture | by Andrew O’Hara | Mar, 2022

Keep up-to-date with out breaking issues

Picture by Steve Johnson on Unsplash

If you’ve been comfy working along with your previous API for a very long time, it may be onerous to undertake a brand new API model; notably in the event you by no means designed for it. On this information, I’ll present you a way to undertake even drastically new iterations, with minimal churn to your code.

Let’s say that we’re a vet clinic, and we now have an app that will get some knowledge from our API, codecs it as HTML, after which prints it to the console. You’ll be able to extrapolate how we would construct a totally useful internet or cell app from this define.

Once we designed this app, we had a really particular thought for a way it might look, so we designed our mannequin and API round these necessities. It really works completely for us, and we’re very proud of it.

However now we now have a brand new requirement: we now have to indicate the cat’s favorite meals. The backend staff already helps this, however it’s solely accessible of their new API model. Lets take a look on the new schema they’ve despatched us:

Seems they’ve made loads of modifications to the brand new schema, and there are a number of points:

  • we don’t wish to assist the brand new values of breed, and so they use CamelCase as a substitute of the snake_case we wish.
  • colors is a set of enums as a substitute of the gray and brown booleans we wish
  • id and ownerId are UUIDs as a substitute of the integers we wish
  • appointments are lacking from the cat schema; they’re solely accessible from a very completely different API operation

Think about for a second that our app is very large: if we had been to undertake this new schema as our mannequin, the modifications can be large, and would require intensive regression testing. We will’t justify that quantity of effort when all we wished was for a brand new property to be added.

If we undertake a few of the principals of Hexagonal Architecture (particularly Ports and Adapters), we are able to insulate our app from most of those points. We will undertake the brand new API model, add the brand new function, and alter little or no of our current code. All of that is potential with an adapter the converts the brand new schema to the mannequin we’re accustomed to.

You’ll be able to design many various adapters to suit a single port

Port

In software program phrases, a port is the interface that defines the contract we wish to have fulfilled.

enjoyable interface Horn 
enjoyable honk()

Adapter

An adapter is the implementation of the port’s interface. There could be a number of variations that may be swapped out and in as wanted, and even on the fly, or layered with useful programming.

class CarHorn: Horn 
override enjoyable honk()
println("Beep")

class TruckHorn: Horn
override enjoyable honk()
println("BURRRRRRRRRP")

Outline our Port

So, how can we use Ports and Adapters to assist us? If our objective is to make use of the identical Cat mannequin as earlier than, then all of the port actually must do is get a Cat by its id. However first we have to reconcile the distinction between the previous integer id and the brand new UUID. Fortunately, the 2 could be simply expressed as a String , so our port will use that. We will afford a small compromise by updating our Cat mannequin to make use of a String id.

Implement the v1 Adapter

Now, lets give you a v1 adapter to implement our port. It’s quite simple, as a result of all it does is delegate the duty to the ClientV1.

Implement the v2 Adapter

Subsequent we have to create an adapter forClientV2. This one is a little more complicated as a result of it must make two API calls and merge the outcome into the unique Cat mannequin. We’ll additionally take this chance to get the brand new favouriteFood discipline from the CatDtoV2.

With a view to really use this new CatsDao port, we should replace our CatUi to make use of it as a substitute of the ClientV1 . Then we should replace our most important methodology to inject an adapter. Then we are able to safely render the cat from both API, and the favorite meals, if current.

So now we’ve efficiently adopted a very new UI with minimal churn. However why did we preserve a V1 adapter?

Cause 1: Security

It’s usually beneficial to nonetheless assist the previous API and use a function flag to slowly roll out the replace or shortly roll again in case of a difficulty.

Cause 2: Legacy Information

If the v2 API doesn’t have entry to all the previous knowledge, then we’ll want a option to fall again to v1 .

To work round these issues, we now have 2 new adapters. The toggled adapter makes use of a function flag to find out whether or not we delegate to the v1 or backCompat adapter, and backCompat will try and get the Cat from v2 , falling again to v1 if not discovered.

Our authentic app had an issue, and the enhancements thus far haven’t modified that. After efficiently releasing assist for favouriteFood, we now wish to assist all the brand new colors accessible within the v2 schema. The Cat mannequin doesn’t assist this, however updating it might complicate our ClientV1.

Earlier than, this is able to have been an enormous downside. However now that we now have the instruments to insulate our inner mannequin from new API variations, we are able to take it a step additional and insulate it from the (now) legacy v1 schema by making the ClientV1 return a brand new CatDtoV1, and updating the adapter to transform from CatDtoV1to a Cat. As soon as that’s finished, will probably be a lot safer to assist this new function, whereas nonetheless supporting each API variations.

Don’t neglect to make the corresponding replace to the v2 adapter.

With this information, it is best to be capable of undertake new API variations with minimal churn to your current code. All it requires is to switch your direct dependency on the consumer with a port, applied by an adapter that converts from the brand new mannequin to 1 you’re comfy with. Please let me know within the feedback if this helps you sort out an onerous migration. Good luck!

For the complete supply code, see the repo under:

More Posts