EAI iPaaS

Hexagonal Architecture–The Great Reconciler?

Published on : Dec 20, 2014

Category : BizTalk Server

Charles Young

Author

This is the second in a series of articles on the emerging world of microservices PaaS and its relevance to Enterprise Application Integration. The other articles are:

1.) Integration and Microservices: Describes enterprise application integration and its core abstraction of messages. Lists some widely accepted principles of microservice architecture and relates these two worlds to the emerging phenomenon of microservices PaaS.

2.) Mediation Models for iPaaS and EAI – This article discusses the current iPaaS market and compares and contrasts common mediation approaches for iPaaS and EAI. I’ll considering briefly how this relates to microservices PaaS.

In an earlier post, I described enterprise application integration and its core abstraction of messages. I listed some widely accepted principles of microservice architecture and related these two worlds to the emerging phenomenon of microservice PaaS. In this post, I’m going to lay a foundation for understanding more precisely the correspondence between application integration and microservices. I intend is to build on that foundation in future posts.

oath of non allegiance

Υπόσχομαι να μην εξετάζω ιδέες με αποκλειστική βάση τήν προέλευση τους, αλλά να συμπεριλαμβάνω ιδέες από όλες τις σχολές και κληρονομιές για να βρω αυτές που ταιριάζουν καλύτερα τα δεδομένα της συγκεκριμένης περίστασης.

 

The Bleeding Edge of Architecture

In 2005, Alistair Cockburn, one of the co-authors of the Agile Manifesto and the originator of the Oath of Non-Allegiance, posted an article on his site that recast an old idea in a new form. The concept of ports and adapters is deeply familiar to EAI developers. The new form, which he called ‘hexagonal architecture’ is widely regarded as the inspiration behind microservices.

Hexagonal architecture is a response to the observation that business logic within a given application domain has a tendency to bleed across architecture layers, ending up where it has no right to be. As Alistair Cockburn puts it:

“The attempted solution, repeated in many organizations, is to create a new layer in the architecture, with the promise that this time, really and truly, no business logic will be put into the new layer. However, having no mechanism to detect when a violation of that promise occurs, the organization finds a few years later that the new layer is cluttered with business logic and the old problem has reappeared.”

To understand hexagonal architecture, we first need to understand what it replaces.

Multi-Tier Architecture

The most common representation for enterprise-level application architectural is the layered multi-tier approach. A simple representation is provided in the diagram below.

Multi-Tier Architecture

In this approach, functionality is separated into at least three layers, or tiers, representing presentation, business and data services. The data tier may be extended to include integration with various applications and systems as well as electronic data exchange with external trading partners. In a sense, a layered architecture of this type is really a very coarse-grained, linear, bi-directional variation of the fundamental pattern of models (data), views (presentation) and controllers (business logic). Of course, we don’t normally think of it like that. MVC patterns are applied at a much finer level within the presentation tier. MVC, in its turn, elaborates the fundamental notion that applications process input to provide output.

The division between presentation tier and data/integration tier can be somewhat arbitrary. For example, implementing an email channel in the presentation tier is much the same as integrating with an email server in the data tier. The choice of tier often reflects a distinction between interchange with direct human involvement and interchange that is entirely automated. That makes sense in many scenarios, but in an increasingly digital world, the model can break down. If we integrate a Twitter feed, is that a channel in the presentation tier or an external event service in the integration tier?

The somewhat arbitrary differentiation between presentation and data tiers can lead to confusion. I often see this in real-world architectural diagrams which extend the basic model by representing additional general purpose capabilities such as mediation, routing, orchestration, monitoring and auditing. These are associated with the business layer because they apply directly to services in that tier, or are used to handle interchange between business services and other services in the adjacent tiers. Where are these capabilities to be placed in the architecture diagram? Should they be at the boundary with the data layer? That doesn’t seem right became they can also be applied at the boundary with the presentation tier. Should they be incorporated into the business logic tier? Again, this doesn’t seem right. They don’t represent business logic.

One way to make sense of this is to think of these capabilities as an elaboration of the boundary between the business tier and its neighbours. This solves the problem of their application to both adjacent tiers and also delimits a space in which the business logic resides, and which provides general purpose capabilities to those services. We might represent this as follows:

Multi-Tier Architecture

Enter the Bus

In service-oriented architecture, the ‘border’ around the business services is widely envisaged as an ‘enterprise service bus’. However, the terminology of ESBs is problematic. We are invited to visualise the bus in terms of data flow through some universal and all-pervasive communication fabric, just like the data bus on a motherboard. ESBs are habitually depicted as communication pipes into which services pour data to be transported to some destination.

In reality, the ESB provides no such pipework. At the risk of sounding crass, that is what Ethernet is for. A better mental picture is that of the bus bar running around all four walls of our business logic factory to which we can connect our services, wherever they reside in that space. The ESB provides general-purpose capabilities that any service, wherever it may be located within the enterprise, can invoke as required. These services facilitate connection, communication, discovery, insight and much more. These capabilities are services in their own right. They implement discrete units of logic that resolve the parameters of communication, facilitate mediation and monitor behaviour.

Conceptually, an ESB is like the Keep of some medieval castle, protecting the architectural integrity of the services that reside within its four walls and defining a workplace for the servants and guards that look after the needs of those residents. Because an ESB is a concept, we are free to select the appropriate combination of software to support its physical implementation. We should never confuse the notion of the ESB with any specific software product. An ESB is an architectural statement of intent and a framework for the embodiment of service orientation.

The sense of data flow on the bus emerges naturally when we consider that services must communicate in order to collaborate. However, communication between services that live ‘on the bus’ (within the Keep) generally requires little or no run-time support from the bus itself. Most ESBs standardise the interfaces and application protocols used by services (e.g., SOAP or REST over HTTP). If one service knows the address of another service and understands how to format or process the messages exchanged between those services, then that may be quite sufficient. However, if addresses are subject to change over time, it may be wise to implement a general mechanism to resolve endpoints. This facilitates dynamic dispatch, making it easier to evolve and change individual services over time. We may need additional general-purpose capabilities to map between different data representations, enforce security policies or monitor SLAs. These are all capabilities which can be offered by the bus for use by business services, as and when required.

Things get more complex when services that live ‘on the bus’ (in the Keep) need to communicate with the world beyond. The ESB generally defines the acceptable approaches and protocols for such communication, rather like the guards keeping watch at the entrance to the Keep and the servants that manage the channels of communication between the residents and the outside world. It is at this level that there is often a need for more sophisticated mediation and routing services. We may need protocol adaptation, batch control, authentication services, sophisticated data transformation capabilities and much more.

One very common pattern in ESB design is to create services whose sole task is to manage mediation across the bus boundary. These are referred to an ‘On-Ramp’ and ‘Off-Ramp’ services[1], again playing to the notion of ESBs as channels through which things flow. When a message arrives at the ESB boundary from the outside world, it may first be mediated by an On-Ramp service which routes it on to some service on the bus. Conversely, when a service on the bus initiates communication with the outside world, it may send the message to an Off-Ramp service which mediates it and relays it on to some external destination.

In real-world service bus design, there may be many situations where the use of intermediary mediation services is deemed top-heavy. In this case, a business service may be implemented to act as its own on-ramp or off-ramp. This, of course, leads to stronger coupling. These trade-offs are as common is ESB design as they are in any other aspect of development.

Equipped with an understanding of the ESB, we are in a better position to evaluate ESB products and frameworks. There are many such technologies available in the marketplace, and much variation in what they offer, so at best we can only speak very generally about the functionality they provide. One common theme is that of service containership. Containership is used to ensure that services can be deployed, versioned and retired freely and independently across the enterprise. For example, some ESB products implement dynamic code deployment via distributed agents so that services can be spun up, torn down and scaled horizontally wherever required. Another role of containers is to provide an environment for each service instance that caters for cross-cutting concerns such as service management, monitoring and security.

Another common theme of ESB products is the inclusion of integration tooling to handle adaptation, mediation and routing between services and, more importantly, across the ESB boundary. This functionality may be accessible through the container. It may be used to implement configurable on-ramp and off-ramp services. In this respect, ESB products are often similar to EAI/EDI products and frameworks. One common difference, however, is that ESB products emphasise highly distributed service environments whereas EAI/EDI products generally promote constrained distribution across a few centralised machines. There are pros and cons for both models, and the choice can be quite confusing for customers.

Hexagonal Architecture

Alistair Cockburn’s great insight was to employ the common notion of adapters and ports, widely employed in EAI and EDI, to recast multi-tier architecture in a new form. He replaced the traditional layered representation with an approach which emphasises the boundary between the application domain (the business logic) and the rest of the world. In this model, we no longer have separate presentation and data tiers. Instead, we have a boundary around the application domain that defines an ‘inside’ and an ‘outside’ with respect to business logic.

His aim, as you will recall, was to use architecture to prevent business logic from bleeding across the layers of traditional multi-tier architecture. He accordingly drew application domains as hexagonal shapes. The number of sides is of no consequence. This multi-faceted approach reflects, instead, the idea that the application logic can communicate with ‘external’ devices and systems via ports. Each port represents a logical entry or exit point to or from the application domain. Conversations take place across ports according to some protocol.

In hexagonal architecture, ports are envisaged as containers for adapters. A single port may contain multiple adapters. The role of the adapter is to implement a concrete protocol by which some external system or device can communicate with the application. We can use ports and adapters to represent our enterprise application as follows.

Hexagonal Architecture

Mediation

I’ve elaborated on Alistair Cockburn’s original representation by depicting an inner mediation boundary into which adapters are plugged. The reason I’ve done this is to clarify the nature of ports. They are used for two purposes. The first is to group adapters according to a common semantics. For example, we might group adapters together in a single port based on their support for customer channels, or because they connect to trading partners or to a database layer, or to line of business applications. This semantic grouping is more nuanced and informative than the use of presentation and data tiers of multi-tier architecture.

The second role of ports is to protect the application domain from the impact of change to external systems and applications that may result in the use of different protocols, message formats, conversational patterns etc. Of course, it is never possible to fully protect the application domain from all changes. However, ports help to ensure that, in the same way that business logic does not bleed into other layers, mediation logic does not bleed into the application domain. Instead, ports do whatever is required to adapt the external world to application domain. Similarly, if we make changes to the application domain, ports minimise the impact on external applications and systems. When changes occur, it is often possible to handle the impact entirely in the relevant port so that the conversation appears to continue unchanged on the other side of the boundary.

Reducing the impact of change in this way is very beneficial. I’ve seen situations where, without this isolation and indirection, the cost of regression testing alone is so high that any significant change becomes economically unviable. This is often the trigger that leads organisations to overhaul their entire approach and adopt a ‘port and adapter’ architecture for integration. With the right kind of mediation, the cost of handling change can be reduced significantly, allowing organisations to be agile and responsive to an ever-changing business landscape.

The reason, then, for illustrating the inner boundary in the diagram above is to highlight the role of mediation in minimising the impact of change and protecting the investments made either side of the boundary. It also illustrates that mediation may occur at both the adapter and the port levels. Consider a scenario where the application domain processes orders sent via different channels. Each channel handles different conversations with different representations of orders. The conversations, however, all relate to the same topic and each order is processed the same way within the application domain. From an inside-out viewpoint, the application domain interacts with mediation capabilities at the port level. However, from an outside-in viewpoint, each channel uses the mediation capabilities of an individual adapter.

One of the features that emerges from hexagonal architecture is an asymmetry between two types of port. Alistair Cockburn termed these ‘primary’ and ‘secondary’ ports. A primary port ‘drives’ the application logic. Conversations with the application domain are initiated by adapters, often as a direct consequence of some external stimulus such as an external system initiating a conversation. Secondary ports are used by the application domain to ‘drive’ adapters. In this case, this is always as a result of the application domain initiating the interaction, generally in order to communicate with an external system or device. Primary ports are coloured black in the diagram above, while secondary ports are coloured grey.

Adapters and Ports

As we saw earlier, the role of an adapter is to implement a concrete protocol by which some external system or device can communicate with the application. The terms ‘adapter’ and ‘protocol’ can bear very wide interpretation. Alistair Cockburn defined adapters in terms of the pattern described by the Gang of Four (Design Patterns – Gamma, Helm, Johnson and Vlissides – Addison Wesley, 1995). This adapter pattern concerns only the conversion, at the code level, of one programmatic interface to another. In the world of EAI and EDI, adapters generally handle a greater range of concerns, including transport and application protocol adaption, batch management, transaction co-ordination and much more. Adapters, then, include a wide range of concrete implementations including Data Transfer Objects, MVC frameworks, ORM, data access layers, services built using Apache CXF, Microsoft WCF and other communication frameworks, and the sophisticated adaptation technologies provided by EAI, EDI and ESB products. These technologies, incidentally, model adaptation and mediation on the upper layers (4 to 7) of the OSI reference model. Increasingly, we are seeing the emergence of ‘hybrid’ adaptation where the adapters and ports may reside behind a corporate firewall and communicate with an application domain hosted in the cloud.

In contrast to adapters, ports are architectural concepts that signify a common semantics shared by one or more adapters. Alistair Cockburn related these semantics to specific topics of conversation between the application domain and the outside world. In my experience, this is by far the most common approach in real world architecture and design. As semantic constructs, ports may or may not have corresponding concrete implementations.

Let’s consider real-world implementations. One EAI product I have used extensively implements primary and secondary ports as configuration items. At run time, this configuration is used to spin up service instances distributed across machines. Each instance represents an endpoint. The port configures this endpoint with a selected protocol adapter and sequential workflows that perform specific mediation tasks on inbound and outbound messages. The port also provides an additional transformation capability that is shared across all the adapters defined by the port. In another example, a widely used communication framework (WCF) allows a single service implementation to be configured with multiple endpoints, each bound to a different mediation stack. In this case, the service itself represents a port.

Alistair Cockburn emphasises another important benefit of ports and adapters. This is the idea that adapters can be implemented as test harnesses and mocks. Adapters in primary ports drive the application domain while those in secondary ports are driven by the application domain. This fits perfectly with the use of test frameworks and tools. It is not my purpose here to discuss testing in relation to hexagonal architecture, but I do want to highlight a further insight. If adapters can be implemented as test harnesses and mocks, this implies that the use cases that define the functionality of the application domain apply at the inner boundary of the hexagonal model. They are, themselves, decoupled and isolated from any of the external systems and devices that the application communicates with. This implies that test harnesses and mocks, designed per use case, can provide a mechanism to detect the bleeding of business logic across different layers.

Hexagonal Architecture and Microservices

At this stage, it should be obvious that hexagonal architecture describes succinctly the central concerns and approaches in EAI and EDI. We can see that it also corresponds well to the notion of the enterprise service bus. However, hexagonal architecture has a third application. It plays a foundational role in microservice thinking and is regularly described as its main inspiration. This may come as something of a surprise, given that several commentators contrast microservices, sometimes sharply, with ESBs and ‘traditional’ integration approaches. What is going on?

To understand this, we must first explore the relationship between hexagonal architecture and microservices. Microservices are discrete services that ‘do one thing and do it well’. They are independently deployable and versionable and can be developed, replaced or retired with little or no impact on other services or dependent applications. To achieve this, we must pay close attention to the very concerns that hexagonal architecture addresses. We must implement effective mediation at the application service boundary to ensure that business logic never bleeds across it. Because the real world is messy and does not adhere to microservice principles, we are forced to adopt a clear architectural ‘inside’ for our microservices and an ‘outside’ in which the rest of the world resides.

Hexagonal Architecture and Microservices

Within the microservices world, there is little need for sophisticated forms of mediation. We adopt a standardised interface such as REST and build our microservices accordingly. Of course, we may still need to transform between different data representations, especially if we compose solutions from microservices built at different times by different teams. Other mediation requirements may arise from time to time, but in each case, we can implement a microservice to handle these concerns. In the microservice world, mediation is best done using discrete, lightweight mediation services. This, incidentally, was exactly the argument promoted by some the leading voices in the emergent world of ESB a decade ago.

Hexagonal architecture naturally supports another microservices principle. Microservices should be organised around business capabilities. Traditional multi-tier architectures group services by functionality. Microservices, by contrast, should be grouped according to the products and services offered by the organisation. Microservice principles advocate cross-functional development teams with product-centric attitudes aligned to business capabilities.

Hexagonal architecture replaces functional tiers with the notion of ports aligned to the semantics of conversational topics. By extension, these semantics are aligned to business capabilities. Consider, again, the example used earlier of a scenario in which orders are received via different channels and processed in the application domain. Each channel handles different conversations with different representations of orders. However, each conversation shares the same semantics. They are all about the same topic. By grouping the adapters for each channel into a single port we can mediate all these conversations to a single collection of order-processing microservices. We have grouped our microservices according to a business capability.

We saw, earlier, that hexagonal architecture allows us to test microservices against specific use cases by ensuring that those use cases are defined and applied at the inner boundary. This is yet further evidence of the way hexagonal architecture supports the principle of business capability alignment.

Reconciling different worlds

Given all this, we may be tempted to think that hexagonal architecture provides the ultimate reconciliation between microservices, on the one hand, and ESB and integration tools on the other. Using the previous diagram, we might claim that microservices describe what goes on in the ‘inside’, while ESB and integration tools should always be employed at the port level. Indeed, that approach would be entirely acceptable in terms of hexagonal architecture.

I can’t see this claim being accepted by the microservices community. However, I think it reasonable to suggest that hexagonal architecture takes some of the heat out of the argument. It invites us to consider that ESB, EAI and EDI are closely aligned to microservices and share much in common. In particular, they share many common concerns which they address in fundamentally similar ways at the architectural level.

As an example, consider the real world example of an EAI product that implements a text-book implementation of hexagonal architecture. The product I have in mind predates Alistair Cockburn’s article, but uses the same terminology and ideas to address the same concerns in the same way. More than this, its ‘inside’ allows developers to create and host services which can be deployed, versioned and hosted as independently as required and scaled horizontally across multiple processes and machines. Each of those services implements standardised interfaces. Mediation is separated cleanly from these services and implemented with ports.

I am quite certain that this product will never be regarded as an example of microservice architecture. Indeed, it would be widely considered as an excellent example of exactly the approach that microservices disavow. In the same way, this EAI tool was regularly singled out by commentators in the emergent ESB community a decade ago as an example of everything they disapproved of. Some of the iPaaS vendors have recently adopted similar arguments against the same toolset. The wheel turns and turns.

To make sense of this, we need only ask what characteristics of the EAI product attract the disapprobation of the microservices community. It clearly isn’t its adherence to hexagonal architecture or to those principles of microservices which it supports. However, it is easy to identify the problem. The EAI product provides a heavy-duty proprietary toolset and a closed-world run-time environment, licensed in a fashion that makes it too expensive for most organisations to distribute widely across the enterprise. It is, instead, almost always distributed over a small number of centrally managed servers. It scales, in part, by implementing complex internal algorithms to squeeze every last ounce of capability out of the available hardware.

If we look at the ‘inside’ of the EAI product, we see further issues. The services must be built using the tooling supplied by the product. This involves the use of a proprietary script language with the ability to invoke code hosted in one of the major runtimes, limiting the languages and tools developers can use to implement custom logic. Communication between services is handled via the same fabric used to route messages to and from the ports. This always involves the use of asynchronous queues and cannot be reasonably claimed to be ‘lightweight’ in the microservices sense.

The EAI product, then, violates several of the principles of microservices while, at the same time upholding several others. It is, however, architecturally compatible with microservices at the level of hexagonal architecture. This indicates that we could, if we so wish, use microservices in conjunction with this EAI tool. Why might we want to do that?

This question, I think, gets to the heart of the matter when looking at integration from a microservices perspective. The previous diagram illustrated the use of microservices in the application domain, only. We have seen that hexagonal architecture admits a very wide range of adapter implementations. From a microservices perspective, it is natural to advocate implementing adapters as microservices. In this case, our architecture looks like this.

Reconciling

It is precisely that this stage that, as an EAI developer, I begin to have doubts. Reservations naturally suggest themselves to me. Microservices should be simple and should focus on a single task. However, my experience of integration is that adapters must often exhibit considerable complexity, not only in terms of custom mediation logic which must be as complex as the use case demands with respect to a given channel, but also at the level of transactional control, batching, correlation and even thread pooling and parallelism. If I adopt a microservices approach, do I try to untangle this complexity by separating out different concerns into different services? If so, how do I wrap all those services into a single transaction? How do I ensure recoverability if any service ‘breaks’. How do I correlate across those services?

It is because these issues are common in the work I do that I invested time and effort a decade ago in becoming a ‘high priest’ of integration focused on a specific product. That product gives me a tool box containing all I need to answer the most complex mediation requirements I meet. I don’t need to write complex code myself. I can depend on a mature codebase from a major vendor. So, if I am to adopt microservice adapters, I want to be sure that these microservices, together, provide me with all I need to continue to address the complex issues in integration. If I am not given the tools in the microservices world, I will look elsewhere. I care much more about getting the job done than I do about adherence to an abstract set of principles. Getting the job done is what pays the mortgage.

I can summarise this simply. When I was a student, more than thirty years ago, I studied the science of photography to degree level (measuring the detective quantum efficiency of photographic emulsions, not learning how to take arty pics). In my first year, we had a pedantic lecturer who endlessly repeated the same few pearls of wisdom. We laughed at him behind his back. One pearl was this. “Your perspective depends solely on your viewpoint”.

I’m older and wiser, and I’ve often had reason to recall his words with gratitude. They illustrate a fundamental truth which we do well to remember every time we are caught in any kind of dispute. Here, then, is what I see as the substantive difference between the traditional EAI perspective and the modern microservices perspective.

Perspective

The tension ultimately reduces to a debate about how to do mediation between the inside and the outside of the hexagonal world. On the microservices side of the argument, Bob, with his inside-out viewpoint, emphasises the elimination of any unnecessary complexity. He wants to ensure that adapters remain lightweight and can be deployed and versioned as independently as any other microservices. Charlie, the ESB guy, is positioned closest to the inner mediation boundary. He considers he has been promoting the microservices cause for the last decade, albeit with a containership model that Bob does not appreciate at all. He can see the need for some complexity in the adapter layer but also gets Bob’s point about lightweight mediation. Then there is Alice. From her outside-in viewpoint, she is thinking about the complex mediation she does every day. The idea of having to depend on lightweight mediation services, or worse still, write her own mediation framework from the ground up, makes her blood run cold.

The next article in this series discusses the current iPaaS market and compares and contrasts common mediation approaches for iPaaS and EAI.