The Power of Adapters in Service Architecture:


When building a service architecture, understanding the significance of each component is paramount. Among these components, the Adapter stands out as an element that often prompts contemplation and inquiry. Questions arise: “Is this additional layer truly necessary?” “Does it introduce undue complexity?” “How does it fit within the overarching structure?” Drawing from extensive experience and insights garnered from industry leaders, this article aims to demystify the role of adapters. We will delve deep into their essential contribution, emphasizing their instrumental role in crafting a responsive, streamlined, and forward-looking service architecture.

From an architectural standpoint adapters are essential for standardisation, anti-corruption and decoupling systems, however from a business perspective they can be the difference between autonomy across disconnected teams through a clean architecture or running a 3-legged race due a complex intertwined integration architecture

A high level view of consumer application and services

Boundaries

A service architecture with different systems/applications consuming a set of services (APIs and events) can have different contexts each with their own specific language, logic, model and actors. Boundaries can be hard to find especially when the problem is stated as an end-to-end solution, for example: when we hear “we need data from system x” instead of “we need to consume this domain data” we imagine a point-to-point solution

One way to imagine boundaries is as shown below, each system is in its own context using the heuristic “new system / application” is a different context. This is represented as a DDD context map like this

Context map

When we add command, queries and events our DDD context map gets richer

Context map with cross-context relationships and see exchange of information via command, queries, events

Key then is to realise these boundaries exist as we draw our end to end solution diagrams and view the world differently – contexts do not go away when our perspective shifts to a specific outcome, boundaries exist and we need ways to ensure things pass in and out of the boundaries without corrupting internal context logic

Solution context contain boundaries

Boundaries are also important because we want the things (technology components) within the boundary to have high cohesion which things across boundaries to be as lightly tied together as possible (loose coupling

A Practical Dive: The Student Management System

Let us use an example of a “Student Management System” which has interface for student query (query), enrolment (command), fetching student related data like courses (query) etc and events for student data changes ( state-change events and data events)

Now, let us imagine we have 3 consumers C1, C2, and C3 with different requirements

  • C1: Bulk data ingestion, lots of reads and require data authorization
  • C2: Requires near real-time events from core system and real-time APIs to query the back end for the latest and it deals with time-critical business functions
  • C3: Is a web or mobile channel which requires real-time reads and commands. Perhaps it needs a one-way command the most on the busiest days of the year ( submit enrolment etc)

Using consumer adapters we can then handle these concerns in the specific adapters instead of baking them in the domain services

  1. Connection: A flawless handshake with the “Student API,” irrespective of the consumer’s quirks.
  2. Security: Tailor-made security protocols for each consumer’s peace of mind.
  3. Performance: Data retrieval and processing that’s not just fast, but also efficient.
  4. Aggregation: Merging data from diverse sources, delivering a comprehensive dataset.
  5. Customization: Crafting data and services to match the exact whims and fancies of each consumer.
A sample C4 container view of consumers, consumer adapters and domain APIs

Using the adapter pattern can appear to add an extra layer and while we recommend being pragmatic about this we recommend serious considering this pattern as it adds flexibility and de-couples the solution.

The key point is that we keep consumer and downstream system logic our of our core domain services especially when building them at the same time. When components are “born together” in projects there is a stronger tendency for the boundaries between them to be more permeable leading to stronger coupling between them. Given the funding models are most often project based vs product based we find having a consumer adapter between an application and a core business service in a domain API can help build stronger boundaries and better separation of concerns leading to lower coupling

Consider in our example, if system C1 was released with Domain API first a then other systems C2 and C3 were released at a later time through two subsequent projects. Without consumer adapters, each system would imp

Without adapters, consumer concerns push into a core business service incrementally which leads to inherent coupling across the consumers through the domain service

Adapters help establish these boundaries and act as an anti-corruption layer across boundaries preventing external formats from leaking into internal implementations and internal system details leaking out to external services

Conclusion

In the sophisticated realm of service architecture, adapters are indispensable. They orchestrate harmonious interactions, tailor experiences, and drive growth. Recognizing their pivotal role, it’s imperative to fully understand, embrace, and leverage their capabilities. Their integration ensures that our architectural frameworks are not just robust for today but are also geared to meet the challenges of the future.

Adapters enforce boundaries and help with context isolation. Directly integrating systems to core services or other systems without adapters can lead to very tight coupling which leads to slower change and poor value delivered to end consumers

Leave a Comment