JavaScript in Plain English

New JavaScript and Web Development content every day. Follow to join our 3.5M+ monthly readers.

Follow publication

Hexagonal Architecture Distilled in JavaScript

Pedro Morais
JavaScript in Plain English
11 min readAug 25, 2021

--

This publication is currently being maintained. Topics that are under change will be indicated by [brackets]. ℹ️

I plan to write this article to clearly share how to understand and implement the pattern Ports and Adapters (Hexagonal Architecture) in JavaScript.

I chose to build this publication in a different format. Developed a repository with the actual implementation of a Blog using Hexagonal Architecture.

The purpose of this repository is to present concrete examples of the concepts I will explain.

1.0 — Component view of the pattern.

The study of this article will work as follows: On this page, I will present a high-level view. Details of the concepts and practical examples in JavaScript will be in the repository:

If any concept is continued in the repository, the link will be at the end of the topic.

Summary

  • Introduction
  • Idea and motivation
    Why hexagon?
  • Configurable Dependency
    A conceptual layer on top of Configurable Dependency
    Inverting and Injecting Dependency with TypeScript
    Now, in JavaScript
  • Hexagonal Architecture
    Hexagon
    [todo]
    Actor
    [todo]
    Port
    About the number of ports
    Clear example of implementation
    Adapter
    Use Case
    [will be deprecated]
  • Conclusion
  • References

Idea and motivation

Published in 2005 by Dr. Alistair Cockburn, the Ports and Adapters architectural pattern comes with the idea of decoupling an application from its technologies — like frameworks, third-party implementations or libs, etc.

Motivated by the infiltration of business logic into the user interface code, the main idea is to define the structure of the application, to run on different drivers. Thus, one of the gains would be the possibility of testing our application more easily, being able to isolate unnecessary external implementations for a given test strategy.

Why hexagon?

The original idea moves to the side of bringing an asymmetry around the concept of application-inside and application-side outside, rather than top/down or left/right as we commonly see in layered architectural approaches.

According to Alistair Cockburn, for these reasons, a square would not be adequate. Geometric figures like pentagon or heptagon are very difficult to draw — so let it be a hexagon.

1.0 — The overrated Hexagon…

Finally, arguments are demystified where the number 6, or the sides of the hexagon, would have some meaning for the concept.

Configurable Dependency

Before we move on to the concept and later its implementation, we should have a clear understanding of the Configurable Dependency pattern.

Configurable Dependency is understood by the joining of two very famous industry patterns: Dependency Injection and Dependency Inversion. In the article Ports and Adapters Pattern (Hexagonal Architecture) by Juan Manuel Garrido Paz, there is a dedicated and well-written chapter on the subject: https://jmgarridopaz.github.io/content/hexagonalarchitecture.html#tc3.

In short, we see Configurable Dependency as the dependency of an object (or a function) on an interface (or protocol). I add the words “function” and “protocol” in the previous sentence, as we will more or less follow this idea in the implementation example of this publication.

When establishing the dependency for a protocol structure, we invert the dependency of that object for another concrete object. We can then inject some concrete structure that respects such interface proposed by the protocol.

A conceptual layer on top of Configurable Dependency

In Alistair Cockburn’s original publication, in the “Related Patterns” section, we have last (at least as of this writing) the topic “Dependency Inversion (Dependency Injection) and SPRING”.

Some more experienced readers, even if they do not yet know the concept of Ports and Adapters, just by adding the pattern’s idea and motivation information, with the brief explanation of Configurable Dependency, probably already predict that to obtain such independence from external technologies proposed by Alistair Cockburn, the solution would be for us to invert and inject dependencies.These readers are not wrong.

Obviously, Ports and Adapters is composed of other aggregated concepts, but I would venture to say that one of its solid bases would be Configurable Dependency. Having this clarity, we follow with an example of applicability, both in TypeScript and in JavaScript.

Inverting and Injecting Dependency with TypeScript

Let’s imagine that we’re implementing a use case — don’t stick to the literal meaning of this concept yet — for a blog.

This use case aims to create a blog post. I will call it “Create Post”.

We know that to accomplish this goal, we need to persist the publication in some database (I could save it in a text file on the file system, but I’ll use database as an example). To invert the direct database dependency, I point that dependency to a property in the class I’m developing. I’ll call it “database”.

1.0 —Example of decoupling a Use Case from persistence code using TypeScript Interface.

This property is fed via the constructor, so if I run the use case’s “execute” method without assigning anything to it, nothing will work. Therefore, when instantiating the example class, we would need to assign to the constructor an object that respects the “Database” interface. So we would have to Inject a Dependency.

Now, in JavaScript

In TypeScript, with features commonly found in object-oriented languages, we managed to have fluent support for the Configurable Dependency pattern. Now, how to get the same result in a language that doesn’t have the native Interface feature?

In the example below, I use the concept of Higher-Order Function, where we have a function at a higher level, which returns a child function, at a lower level.

The strategy here is to use the higher-order function as the “constructor” feature of classes. At the time of writing this publication, ES2021 has had the native class feature for quite some time. However, I decided to use this example to get closer to the code writing of most purist JavaScript developers.

Note that I overcome the lack of the “interface” feature, building an object called “Database”, contained in the first line of the figure. This object has a method, which when executed, throws an error informing that there is no implementation in its execution.

1.1 — Example of decoupling a Use Case from persistence code using JavaScript function parameter.

This format contains numerous problems. I attribute this Interface simulation as the default value of the “database” parameter. If the client injects a dependency that doesn’t completely respect the structure described in “Database”, we wouldn’t have the compilation error warning by the language. This is because JavaScript is an interpreted language, not a compiled one.

This is not the safest way to simulate an Interface, but it is what we have for now in JavaScript.

Given the alerts, I perform the same two steps of the example in TypeScript, differing only by the lack of the “this” reference.

Hexagonal Architecture

Hexagon [todo]

Actor [todo]

Port

Ports are at the edge of the hexagon and are the means of communication, both from the external world to the hexagon, and from the hexagon to the external world. Therefore, a port can be seen as something that carries the purpose of an interaction.

As it is described as a purpose of an interaction, the nomenclature will have “for” at the beginning, indicating what the interaction will serve.

Taking an arbitrary example, we could have a port on the “left side” of the application, indicating the purpose of external contact with the hexagon. If we are talking about a sweepstakes application, a port to access the hexagon could be “for-raw-random-numbers”.

1.0 — First macro view of the Hexagon with the Ports.

If the code inside the hexagon, with the purpose of drawing random numbers, needs to make a connection with a technology that is outside the hexagon (database for example), we could have a “for-save-draw-record” port.

About the number of ports

In the original publication, Alistair Cockburn is not rigid about this, revealing that we could have:

  • A single port for all external world interactions with the hexagon, and one port for the hexagon interactions with the external world.
  • Different and specific ports for each interaction with the hexagon and equivalent for each interaction of the hexagon with the outside world.

Also, the same says that neither case seems ideal. In reality, ultimately, this will be a matter of the engineer/architect’s intuition.

In the example repository of this publication, I use a web port (which would be more representative of the contact of the external world with the hexagon) since we cannot design a simulation of a function’s interface in JavaScript. Perhaps, in a different approach, you could have written the code that is inside the hexagon (in this example, the use cases) as JavaScript objects with methods and properties, closer to a class approach.

Another port I use is for the purpose of managing posts. On this port, I gather all the operations necessary for that purpose. On this side because in the implementation, I partially used the concept of the repository pattern.

I can’t say that the examples app would only have two ports. If its extension were needed, it would add more hexagon contact ports with the outside world, depending on the need for more repositories.

However, as specified in the original publication, this is at the discretion. In another project I have open source on my GitHub, I follow an approach where I declare a specific port for each interaction with the hexagon, and equivalent in hexagon communication with the outside world. This example can be found bellow:

Clear example of implementation

Commonly, ports can be written with the Interfaces feature, commonly found in object-oriented languages.

In this example, I use JavaScript objects, where the properties are the ports, and the implementation throws an error indicating that there is no concrete implementation there, just a port demarcation.

A similar example was described in the “Configurable Dependency” section.

For detailed reading with example JavaScript implementation, see: Ports — Hexagonal Architecture Distilled →.

Adapter

When teaching about this topic, I like to use the following analogy:

The port specifies the language that is allowed to communicate with the hexagon. The adapters serve to convert the language of the actors, into the language understood by the hexagon, specified by the port.

Tangible with the context of the Hexagonal Architecture, the adapter will be the implementation of a specific technology for what the port specifies.

Given a port named “for save a draw record”, we could have an implementation for a MongoDB, MySQL database and a mock for testing.

1.0 — View of the relationship between Hexagon, port and adapter.

An important insight to take with us when implementing the Hexagonal Architecture is that at a minimum, each port must have at least two adapters.

Is this a rule? If we pretend that it is not, we can understand that it may not make sense to use the default Ports and Adapters. Maybe you are accidentally creating too much complexity and it makes sense to just call the external technology function directly.

Therefore, whenever we design the implementation of our adapters, we could consecutively already have the answer whether or not it makes sense to use the Ports and Adapters pattern. Here’s the tip.

This topic is a pointer to the GOF Adapter pattern, so the main specification is the same.

“Convert the interface of a class into another interface, expected by the clients. The Adapter allows classes with incompatible interfaces to work together — which would otherwise be impossible.”

In our JavaScript context, we may also use functions for this purpose.

In this publication’s example implementation, I use JavaScript’s Closure functionality, and the Higher-Order Function technique for injecting adapters into code that is inside the Hexagon. These, in turn, depend on Ports, which are represented by a literal object with no concrete implementation of the features.

When we design a project this way, we can inject adapters according to context and intent, isolating resources. For example, if our hexagonal code needed to use the network protocol to obtain some resource, in a unit test context, we could isolate this very expensive resource, and inject a fake adapter that returns a result compatible with what we want to handle in the code.

For detailed reading with example JavaScript implementation, see: Adapters — Hexagonal Architecture Distilled →.

Use Case [will be deprecated]

In Alistair Cockburn’s original publication, the reference to the inner part of the code was not given as a Use Case.

I decided to integrate the concept in this publication, because the author himself has effectively contributed to the definition we have of a Use Case today — read: Writing Effective Use Cases, by Alistair Cockburn.

Furthermore, the original definition of the concept follows the statement that Use Cases can be described as functionalities that a system must respect. The mold of your identity.

By definition, we have technologies from the outside world that interact with the Use Case represented through Actors.

This view is affirmed in the Use Case diagram concept, later suggested by Ivar Jacobson in the infamous UML notation language.

For a long time (and even today) the software industry has kept Use Case writings with undue knowledge of their Actors.

So, we fit Ports to demarcate what Actors at a certain point in the code should do. With Adapters, we specify how to do it.

If we look closely, the Actors of the UML Use Case diagram would be a great indication of Adapters of a Hexagonal Architecture.

For detailed reading with example JavaScript implementation, see: Use Cases — Hexagonal Architecture Distilled →.

Conclusion

With the Ports and Adapters architectural pattern, we develop healthy, maintainable applications with well-defined responsibilities.

A big tip: Don’t use this architectural pattern without creating unit tests.

Going against the above sentence would be almost like a death sentence.

One of Alistair Cockburn’s motivations would be to develop implementations of “mock” external technologies to create true unit tests. Tests that test a single conceptual functionality.

Best practices in Software Engineering and Architecture are guided by more best practices.

References

More content at plainenglish.io

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

New JavaScript and Web Development content every day. Follow to join our 3.5M+ monthly readers.

I write about Architecture and Software Engineering. https://github.com/pedromoraisf 🇧🇷

No responses yet

Write a response