Hexagonal architecture in JavaScript applications — and how it relates to Flux

Image for post
Image for post

Flux is a ‘mainstream’ approach of data management for React.js-based views. While I find Flux useful, I don’t think it is always needed — in fact, it can be a bad fit for your project if you follow it blindly. Dan Abramov has some excellent points about the topic and provides some basic guidelines when Flux can be beneficial for you — and I agree with most points he made in his article.

There are alternatives to Flux when it comes to solving data management and achieving maintainable code. Flux is great, but it’s great for use cases Facebook had. It absolutely shines when you have multiple representations of the same data — single source of truth principle helps you avoiding headaches with denormalized state.

I’d like to show you such alternative. It’s called hexagonal architecture and it proved its worth for me years before Flux even existed. I’ve used it in times where jQuery with some static templating engine or JavaScript framework like Angular was a way to go when implementing rich frontend solutions. It still defends itself in a world when React.js is a thing and there are many excellent ideas flowing around in the community. And the best part — understanding hexagonal architecture is a great asset when working with Flux — it tackles the same problem from slightly different angle and brings a lot of insight. In fact, my first journey with Flux was when I’ve implemented it by an accident in an app written with hexagonal architecture principles in mind.

Hexagonal Architecture (also called ports and adapters architecture) is an idea about structuring your application around the domain code. The main goal is that your business logic is the most important part of the application — and it should be very loosely coupled with technical details like database or user interface. The rule of thumb is — if you can test it easily with simple assertions, it is nicely written business logic code. In most cases, such business logic is closed in a form of use case — an object describing one business process in your application. Such use case can be named like RegisteringProduct or PayingForOrder — so you can clearly see from name which business process is represented by such use case.

Of course software is all about side effects — you manipulate data to eventually display it on your screen or store it in a database. So having just an use case is great (because you can test early that your business logic works with stubbed data), but to make it work you need more than it.

That’s why you need an abstraction for performing side effect like that. The hexagonal architecture has an idea of ports, which basically represents such side effects. So, you can write something like this:

As you can see, there is a ports dependency which is used to communicate about business situations that should be handled in a more technical way. For example, cartItemAdded can be interesting to handle on the user interface side (increment the cart counter?) and cartItemsConfirmed can be interesting for router to route you out of the current page and go to checkout.

Passing messages through ports object is often replaced with different technical solutions — in the past I’ve used aspect-oriented programming which allowed me to write rules like “after addItemToCart method is called on the useCase object, do something”, where those rules were defined outside the use case. Then I’ve switched to global event bus solution to allow multi-hex communication in an easier way. But there is always an object (often called glue or [more fluxy] dispatcher) which is responsible for defining what to do with outbound messages from the use case and inbound messages to it.

The architecture is named hexagonal because there is an use case (hex) and multiple “sides” (ports) from which messages can go into or out of the use case. Hex (6-sided) is just because it sounds cool — there can be 2, 8 or 42 sides ;).

Let’s see how ports can be implemented:

User interface, or router, or database, or Facebook are so-called adapters. They represent side effects — Facebook adapter can post something on Facebook, User Interface adapter can be a React component. They are concrete implementations — you can see that on the ports level, whether React.js is powering the userInterface adapter is abstracted away. Think about it — actually you can take your use case implementation and drop it to your Node.js backend — just change adapters and reimplement ports and your business logic is reused.

Many use cases, ports and adapters forms an app. Such app is responsible for initialization of your pieces and orchestrating start. Let’s see:

You can imagine that from Product List in the shop you can click the “Ask a question” button and create the customer care ticket this way. There is also the cart management feature. Adapters can be re-used, so here the same user interface is being an adapter connected to two use cases.

This architecture is great if you want to expose your business rules — and it is nearly always the case if your business logic is somehow non-trivial. In terms of data, use case also centralizes your application state — just like stores in Flux do. It also switches your code structure to represent features — having your use cases focused on one user story has an interesting side effect of looking at file names and just knowing what your application has in terms of software functions.

It is better suited for more smart frontends, where you are really doing something on the client side. It is propably an overkill if you happen to just have an user interface which is delegating everything to your backend and updates state by consulting backend.

You can just constraint yourself and use a flavor of hexagonal architecture which is called Flux.

Wait, what?

Yeah, it’s not a typo. Flux can be considered a flavor of hexagonal architecture!

  • Use case is basically a Store. Methods of an use case is code responsible for processing actions. Such approach is used by Alt library, for example.
  • Ports (it’s inbound messages part) is a dispatcher. It is reponsible for communicating use case that some action happened. Just like dispatcher is passing actions to store.
  • In Flux there is no concept of outbound methods from ports. Instead, the whole use case (store!) state is passed to all listeners. So there is also no processing of outbound messages — just raw state gets passed in. If you just simplify your ports to one method, notifyAll and pass all state from use case, it’ll be the same.
  • There is no concept of action creators in hexagonal architecture, but it can be re-created easily — just instead of publishing an event through an event bus from an adapter, just extract it to a function which adapter will call.
  • Actions are implicit. Basically publish arguments are actions — first argument is type, rest of arguments is data passed with an action.

I’ve come to Flux from hexagonal architecture. Approaching Flux knowing hexagonal architecture before allowed me to solve many conceptual problems — like I have no problem with different clients than user interface in Flux loops, or having multiple Flux loops — one for UI and one for backend, sharing store. I like hexagonal approach since it does not need supportive technologies if you don’t want to achieve one-way flow — but one-way flow is also a good practice in hexagonal architecture! It’s called use case roundtrip and it’s basically an idea that adapters should not refer to ports and use case directly — instead it should use different kind of communication like events. Flux just makes it very important piece of architecture!

And, after all — it’s good to know many concepts and approaches when developing your software, right?

  • YouAreDaBomb — library that has been used to implement AOP in JavaScript. Used by ports (called glue) implementations.
  • eventing-bus — a dead simple implementation of event bus I’ve used in code snippets.
  • Alistair Cockburn’s Post — a totally awesome resource about hexagonal architecture. I’m quite sure (but I can’t find it) Alistair is the one who invented it.

Hexagonal architecture can be a good alternative to Flux — or it can be just an enlightening approach to understand Flux better. I’m very happy with both approaches and I find pros and cons in both of them. I hope the knowledge about this cool, old approach to structuring your application will come in handy.

If you want to talk about architecture (or anything, really :)), just drop a comment. I’m also available on Twitter. If you like my writing, you can also read Arkency Blog (where I write more about Rails) and React Kung Fu (where I write about React.js) where I also publish.

Have a nice day, and good luck with your applications!

Image used at the top of this blogpost is made by gfpeck who kindly allowed it to be used here. Thank you!

React.js evangelist. Writer (4 books, Frontend-friendly Rails being the newest one). Dreams to make software more reliable and just _better_.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store