Hexagonal Architecture for cloud ecosystems

Global Technology
McDonald’s Technical Blog
5 min readDec 5, 2023

To help manage complexity in the cloud, McDonald’s developers are using Hexagonal Architecture patterns — read on to find out exactly what this solution entails.

by Adam Rice, Manager, Solutions Architect

As more and more companies undergo digital transformations and system modernizations, developers are faced with growing complexity around integration across a variety of distributed systems.

In a multi-part blog series, I will be detailing a potential solution to help manage the complexity in a cloud ecosystem: Hexagonal Architecture.

I am going to explain:

  1. What Hexagonal Architecture is
  2. What makes up the architecture

What is the Hexagonal Architecture
A Hexagonal Architecture is an architectural pattern that separates external systems from the core application.

The idea is simple. You start with a hexagon. Apply ports and adapters, right?

A hexagon has six sides. Theoretically, our shape could have 100 sides to explain all the interactions in an application.

The hexagon shape, itself, does not mean anything. It just provides a clean way to discuss and explain the ports, adapters, and the domain of an application.

This shape provides a way to explain small bites of the application flow and not overwhelm the audience with the whole picture of an application. It essentially restricts the designer to design or explain small, easily understandable pieces at a time.

Let’s start with the inside
The application domain lives inside of the hexagon. When we say domain, we mean that principles of the Domain-Driven Design (DDD) have been followed, and our business logic does not leak outside the hexagon. For context, DDD:

  1. Focuses on the main problem by defining a model related to a specific piece of the business.
  2. Uses a common ubiquitous language all team members can understand.
  3. Defines a bounded context in which the domain model is encapsulated.

Following DDD principals, for the sake of this article, I came up with the following domain, using the process below.

Let’s say we are building a new application that allows users to upload files through a website to a central repository for sharing.

Here are some of the basic application requirements:

  1. Files are uploaded by an authenticated user via a website.
  2. Files are uploaded for programs, or another way to say it is for a purpose.
  3. Programs/purposes are a set of preconfigured file specifications that a file must adhere to.
  4. The program rules specify things, like:
    — Types of files that can be uploaded
    — Number of fields
    — Other requirements, like encryption or zipped files
    — The files must adhere to certain specifications to be accepted.

5. Users must be given access (authorized) to upload files for specific programs.

Back to the domain
The domain represents the key business logic of the application, which allows users to upload files to the repository for sharing with other parties. Note, the domain below only covers the uploader, authorization of the uploader, and the file specifications of files to be uploaded.

The blue rectangles are called entities, and together with the blue fields represent the structure necessary to satisfy our function requirements.

A more exhaustive domain model could include the downloader and file-configuration details of the files uploaded or downloaded, as well as possible data-quality configurations applied. It can be argued that this can all be further divided into sub-domains, but for the sake of brevity, we will stick with the current example.

Logically, our hexagon now looks like this:

Remember what we said earlier? One of the principles of the Hexagonal Architecture is that the domain does not leak outside the hexagon or need to know anything about the outside world.

At this point, we could theoretically write code that satisfies the basic requirements of this application, from a business-logic functional perspective. This would be the purest form of application code development. However, this doesn’t do much to help us, as the business logic is wrapped up inside the outer bounds of the hexagon.

We need some inputs and outputs, so we now make some assumptions about how we will interact with our domain.

In the simplest form, the assumptions sound like this:

  1. Data is submitted by a user in the form of a request for information or a file to upload. (Inputs)
  2. This data is verified, transformed, and stored somewhere. (Outputs)

We need to interact with this domain so that it can do its job, which is to authorize uploaders, accept files, and check file specifications (based on programs/purposes) for validity.

Let’s take a quick time out, because the above two steps bring up another benefit of this architecture. In this pure form, unit tests or Test-Driven Development (TDD) can be implemented.

Writing automated unit tests that can run while in development or while making enhancements can reduce the risk of introducing bugs and increases the quality of the code, especially if the unit tests are run as a part of checking-in code and deployment activities (think CI/CD).

If you are following TDD, you write a unit test in code first, before writing any functional code. The test will fail because you have not written any functional code. So, then you write the functional code that satisfies the test. Then you write the next test, then functional code, then test, and so on.

That’s it for this week. Now that we’ve established what Hexagonal Architecture is and created our domain model, next week, we’ll explore how we can connect ports and adapters so the architecture can begin managing complexity.

--

--