How Ports & Adapters architecture can reduce cloud (and other) vendor lock-in

Lindy Hutz
Team Rockstars IT
Published in
4 min readFeb 24, 2022

It’s an often-heard objection in a cloud migration: “but what if I want to switch cloud providers later on?” Whether it’s Azure, AWS or Google Cloud Platform (GCP), it can be frightening if your applications are stuck in one provider but after a while, you’re not so pleased with them anymore and want to change. This is also known as vendor lock-in. Luckily there are ways to reduce its impact.

When moving to the cloud, you’re always committing to a cloud provider to some extend. Especially if you’re developing serverless and relay many responsibilities to the provider. This commitment is not the end of the world because by moving the responsibilities, you’re also saving a lot of time and effort.

Also, in the above-mentioned situation, it’s possible to switch providers at a later stage. I’ve experienced companies gaining so much financial profit from another provider, that even with hundreds of applications, it was still profitable to switch. And if you managed to not be that vendor locked-in to begin with, your profit is only going to be bigger.

There are several ways to keep this commitment to your provider low. Various tools can help you with this, such as Docker of Serverless Framework. Today, I want to focus on how you can achieve this when designing the architecture of your service.

Ports & Adapters architecture

You’ve probably already heard of the Dependency Inversion principle (DIP). In many implementations, DIP means as much as “make sure that your classes are depended on abstractions instead of concrete details”. Ports & Adapters architecture (also known as Hexagonal architecture or Onion architecture) raises this principle to a higher level. Literally. Because where DIP is often applied at the class level, Ports & Adapters applies DIP at the architecture of a service.

In concrete terms, Ports & Adapters architecture means that the core of your service, where business logic lives, works agnostically. It is only called upon by driving adapters (also named primary adapters) through ports. The core itself calls upon its dependencies via ports to driven adapters (also named secondary adapters). This means that the business logic of the application is unknowing of how it’s called or what it’s calling. It only knows that it’s being called by something, whether that is an Android app or a console command.

Source: Ports & Adapters Architecture

Okay, so how does this work in practice? Imagine above is your application and you want to send notifications. According to Ports & Adapters architecture, you write an interface as a port and you would for instance call it INotificationService. You can then initially implement this interface in an SmsNotificationService but could also later on switch and write an EmailNotificationService. These implementations are both adapters of the INotificationService port. Also, in the EmailNotificaitonService you can initially integrate with AWS’ Simple Email Service but later on again switch to Sendgrid. You only have to write a new implementation of the INotificationService but don’t touch the business logic reducing the chance of adding bugs. Through configuration, you can keep both alive and use one or the other.

This is the Ports & Adapters explained in a nutshell. If you want to read more about it, I can recommend you read the original explanation of Dr. Alistair Cockburn. Also, the tad bit more pragmatic explanation from Herberto Graça is very handy.

How does Ports & Adapters architecture help against Vendor lock-in?

With Ports & Adapters, the business logic knows that it’s being called by a function, but not whether that is an AWS Lambda, Azure Function of a GCP Cloud Function. Additionally, it only knows what its own intention is, like saving an object, but now how. That “how” can be AWS RDS, Azure SQL Database or GCP Cloud SQL.

If you’ve chosen one cloud provider and later on wish to swap to another, you only have to re-implement the relevant adapts. The business logic of your app remains intact and that is probably where the most complex logic is at.

Other advantages

Ports & Adapters is not only useful at reducing vendor lock-in with cloud providers, but it helps reduce vendor lock-in with any integration. Examples are your Content Management System (CMS), email service or order service.

Ports & Adapters also helps to reduce lock-in within the same cloud provider. Say you currently have a NoSQL implementation with AWS’ DynamoDB . The integration is fine but your company grows and gets the requirement to become auditable. You could add extra logging to record every action being done to the database, but you could also think of other AWS services like Quantum Ledger Database (QLDB). The only thing you need to do as a developer is to re-implement the existing ports and your application with function the same as before.

Downsides

Unfortunately, there isn’t something like a golden hammer that is the solution to any problem. Ports & Adapters isn’t that either. Imagine an application where all dependencies are on cloud services and the application does little more than pass information from one cloud service to the other. If you’re migrating from one cloud provider to another, you’ll find that “only rewriting the adapters” turns out to be practically rewriting the whole application.

Conclusion

Developing in the cloud can be daunting but luckily there are some best practices that you can follow to build in some more certainty. Ports & Adapters architecture is one of them. If this interests you, I can wholeheartedly recommend reading the added links.

--

--