A Tale of Four API Designs: Dissecting Common API Architectures

a-tale-of-four-designs-nordic-apisIn the world of APIs, the way we design and implement code is of paramount importance. Starting development without a proper architectural design perspective, approach, or consideration could lead to unneeded redundancy, complexity, and purposeless constraints. By implementing one of several common design architectures, however, we can guide our API style in a more cohesive direction, decreasing development time, increasing developer efficiency, and creating a streamlined API life cycle.

Four Approaches, a Unified Theory

In the classic Urdu tale from which this piece takes it’s name, “A Tale of Four Dervishes,” a despondent king named Azad Bakht leaves his decadent castle in search of wisdom. During his journey, he encounters four wise men, each equipped with a unique experience, history, and viewpoint on the world at large. Their stories guide him to happiness, setting him on a prosperous path forward.

Likewise, in the world of API design, there are four primary architectural design styles, each with their own user experience, their own history, and their own approach to the world of web applications and API integrations. Though these designs are not mutually exclusive (a topic we’ll discuss later in this piece), understanding how they differ from one another can help inform your decision as to which design approach you will take, and whether your choice will set you on a similar path of happiness and prosperity.

Uniform Resource Identifier (URI) Design

The URI style is a very common design paradigm — due to the relative simplicity of invoking requests through common HTTP operations, developers often prefer the system over others for the sake of their own workflow. Many of the most basic operations a developer could want are mapped directly to the HTTP methods and functionalities — create, read, delete, and update invocations are all tied to built-in HTTP protocol functions, creating an extremely simplified set of operations to act on the relatively more exposed nature of an object/resource-oriented API style inherent in the URI architecture.

Additionally, URI-centric designs have an in-built propensity to self-document. As the standard for URI designs include so-called “hackable” systems — or systems that allow for end-user manipulation — these APIs are often extensibly changed and manipulated, resulting in a relatively large amount of user documentation and hotfix possibilities.

While the URI design is common and has many good facets, it is far from perfect, and many of the failures inherent in the design almost demand other, more extensible designs be integrated as a partner to the URI (or a replacement all together).

Simplicity might make a system easier to manage and implement, but it doesn’t necessarily make it useful — an API that is not extensible is an API that is dead in the water. When trying to make an API with a complex series of interactions and invocations using such a restrictive system, simplicity is soon thrown out the window. API designs become increasingly complex. The learning curve that was once a benefit is increasingly a disadvantage, and what are simple requests in other design styles soon bloat to multi-request, multi-invocation chains.

Still, with all its inefficiencies and drawbacks, the URI design is a decent enough choice when it’s known that all interactions with the API will take place over HTTP — the best example of this usage is the REST API design architecture. Given that the URI design is unecesarily complex when the API is expanded beyond simple, single purpose, or limited scope applications, in most use-cases it is best paired with other designs.

Event-Driven Architecture (EDA) Design

In part a response to the extremely limited nature of the URI design, the Event-Driven architecture has become popular in recent years as developers aim to create more responsive applications. The key difference between the Event-Driven approach and its contemporaries is that this approach is a two-way street. Whereas other systems rely on either the client or the server listening and responding to events in a previously agreed upon relationship (typically one that places one side as the “actor” and the other side as the “acted upon”), the Event-Driven design requires both client and server to listen to new events.

This, along with implemented support for asynchronous messaging — messaging done via a system of queues, removing the necessity for a reply from either partner to continue sending and receiving — makes for a very powerful API indeed, capable of complex invocations, large data transfers, and innovative transport methodologies.

As with other API designs, the Event-Driven Architecture can be operated over the HTTP protocol stack utlizing both a header and body (in this case an event header and event body). The full potential of the design, however, truly comes forth when used over other methodologies and systems. As such, HTTP implementation is often eschewed in favor of more powerful transport protocols. The WebSocket protocol, for instance, is an Event-Driven system that has been largely incorporated into the HTML5 specification, allowing for the transfer of large amounts of data with relatively minimal overhead using asynchronous communication.

One of the most popular examples of the Event-Driven style is Java Swing. The Java Swing API, designed specifically to create a graphical user interface for Java programs, utilizes an API nomenclature that delineates between “ActionListener” and “ActionEvent”. One of the common examples used to demonstrate the reactive nature of Java Swing is the creation of a button that a user can push, triggering a reaction from the server and rendering a message for both the client and server in part:

public class FooPanel extends JPanel {

public FooPanel() {

super();

    JButton btn = new JButton("Click here!");  
    btn.addActionListener(new ActionListener() {  
        public void actionPerformed(ActionEvent ae) {  
            System.out.println("Alert: button has been clicked.");  
        }
    });  
}

}

Using this anonymous class format, an almost infinite number of reactive options can be provided to the server and client, allowing for complex interactions in a relatively small number of invocations. Like the URI style, however, there are serious limitations to the EDA style — because it utilizes additional protocols and systems to communicate, it requires far more infrastructure than the URI system.

While this design choice is perfect for mobile applications or those within the broad Internet of Things (IoT), the complexity of the system can be wasted if the API functionality is incredibly limited. If your API is simply a request/reply oriented design, implementing an EDA system as a sole choice might be more trouble than it’s worth.

Hypermedia Architecture (HA) Design

The Hypermedia Architecture is a middle ground approach between the Event-Driven Architecture and the URI Design — whereas the URI design focuses largely on objects and the requests for them, the Hypermedia Architecture focuses on tasks and the flow between them. Think of a Hypermedia API as a map — when navigating the streets, a map can give you navigation options to a specific route, and a specific methodology to get there. Similarly, a Hypermedia API helps navigate workflow and provides a feature-rich template for information requests and functionality invocations.

In the context of the broader world of API methodologies, the Hypermedia design is special. While other media system rely on defined URIs, developer-specific documentation, and stringent strictures on message structure and organization, Hypermedia architectures create their own dynamic URIs, utilize link semantics based heavily in media and evolving designations, and utilize free-form message structures as determined by both the developer and the needs of the system.

Most importantly, however, Hypermedia designs are conceptually oriented towards services with an exceptionally long lifespan. Because the API system itself is designed to evolve to serve the needs of an ever-changing, ever-growing user base, a Hypermedia API system could theoretically evolve for years after its initial conception, supporting applications developed both in its infancy and in its later years.

The caveat to all of this, and it is a rather large one, is that a Hypermedia API design is just that — a design. An API designed in this way functions in its own little universe, with its own strengths and weaknesses. Because of this, there will never be truly robust and mature support or tools for a pure Hypermedia system unless the developer specifically constructs them. Despite this — or in spite of it — some developers have attempted to create standards, protocols, and systems (such as HATEOAS) meant to tie into general tools to fill this gap.

Tunneling Style

The oldest of these four styles, the Tunneling Style, functions as a system of Remote Procedure Calls (RPCs) organized in an XML message format. Tunneling APIs allow for localization of content, where RPC calls are used by distant hosts to request access as if the server providing the data is local — this is achieved by the generation of proxy code based on message formatting handled by a variety of providers.

Additionally, because the Tunneling Style utilizes various transport methods as a base for higher-level transport, Tunneling Style APIs are free to use a larger variety of protocols, sockets, or systems for encoding, low-level transport, and communication, such as JMS or TCP/IP, than other design choices. This design format, regardless of transport, is called “transport agnostic” — the system doesn’t care how the data is encoded or transported, as long as it’s compatible with the method of physical communication and the other party is able to utilize it.

The most famous of API formats utilizing this style is the SOAP architecture, which is largely designed for verified transactions (i.e., cases where the client must be authenticated or verified, and transactions must be authorized) and large data handling (such as media processing or sharing). The message format in the SOAP architecture is slightly different from standard Tunneling Style APIs, however, in that the message format for SOAP is codified and restricted under the Web Service Definition Language (WSDL) specifications.

There are several significant drawbacks to utilizing the Tunneling System. These systems are often heavy-weight, creating extremely verbose requests and responses. Tunneling Systems — SOAP in particular — are often far too complex for many applications where systems like the Uniform Resource Identifier design would be more effective. Though this isn’t a problem per se when dealing with single applications, suites of applications or network services can very quickly explode in complexity and in effort needed to maintain and support.

Most significantly, Tunneling Systems expose security in more direct ways than other systems. SOAP users generally utilize functionality through POST requests. Since these requests are folded into an envelope for transport, it’s hard to determine whether or not the request is for data or for a malicious purpose. This is rectified with security, but this is an additional layer of heavy service over an already heavy design — other systems, such as REST, can utilize requests in the GET format, limiting their access to simple retrieval.

Many of these cons can be reduced somewhat by simply controlling the way your API is used and implementing proper security procedures, but all in all, the design is inherently less secure than others. This arises from the simple nature of purpose — SOAP was designed for a distributed computing environment, not a point-to-point environment. API calls and functionality often fall into a strange “ad-hoc point-to-point” gray area of network functionality, making SOAP use in the modern era a relegation of special purposes and functions.

A Hybrid Approach

In some cases, a single homogenous approach is not an effective one — systems evolve and change in functionality and demands as the user base itself changes, and what was once appropriate for the system at large can quickly become obsolete. Accordingly, as systems become more complex, so too can your API, moving away from the homogenous design choice and more towards a heterogenous design.

Though each of the four designs we’ve discussed in this piece are effective in their own limited range of applications, they are only a single piece of the larger puzzle — each has their own caveats, their own strengths and weaknesses, and their own limited range of appropriate applications. The good news is that they can be very easily hybridized if the API is designed in such a way to support it.

Let’s take an example API. Assume we are developing an eCommerce API designed to handle large amounts of user data, generate user reports, and tie into credit and debit services of major banks. If we were designing our API in a homogenous way, we could very easily choose a single style that does everything good, but only a few things great. In this case, the clear choice would be the Tunneling Design, due to the SOAP architecture providing client authentication and authorization through both the in-built communication methodology and the various support systems within.

What if we were to design our API in a heterogeneous way? Our system would be far more complex, for sure — but what would be the benefit of this complexity? Firstly, your user experience would be far different. SOAP (as with other Tunneling Designs) is slow and heavy, and not necessarily geared towards mobile devices. If we design a mobile banking system focused on a Hypermedia system, we can create a fast, friendly mobile experience. We can then utilize that system to tie into a direct URI API to communicate with banks, limiting potential security issues and creating a buffer between the user and the financial institution they are contacting. Finally, we can take the report generation and plant it firmly in a Tunneling API, allowing for long-term data collation and comparison.

The caveat of the approach, however, is that complexity is not always a good thing. A system like this, while providing a better experience for the mobile users, a faster experience for all users, and a more secure interface, is extremely complex for a developer to work on. You’ve essentially created three micro APIs tying in to a central service; this is not necessarily a bad thing, but it is far more complex than a single choice, and the repercussions should be considered with practical sincerity.

Conclusion

We’ve discussed a variety of design choices, each of which present some strong strengths and some serious weaknesses. While each of these systems is powerful in their own right, creating a heterogeneous system can provide a far better API in the long-term — at the cost of additional complexity.

Functionally, however, only the developer of an API can know their best approach. Taking into consideration these points can help make an informed choice, but ultimately it will be your purpose, your knowledge of the user base, and your in-depth analysis of the needs of the system that will determine your architectural design.

Integrate these designs early into your API, integrate them properly, and continue to support them — if you do so, your API will absolutely flourish in the wide, wonderful world of the world wide web.

Further Reading