“A place for everything, everything in its place.” – Benjamin Franklin
I defined “What is an API?” in a previous post, but to truly understand APIs, it is important to explore a few of the most relevant types of APIs that have emerged over time along with their benefits and implications.
APIs are created in many circumstances that can be summarized across the style of architecture or language as well as the method to access the API. These are not mutually exclusive characteristics and there are some overlaps (such as APIs that implement ways to communicate), but this breakdown provides a good perspective of the API landscape.
Types of APIs
Each combination of style and access method has its own benefits and implications. The style of the API will impact its usability across various consuming architecture/language models (paradigms) and may influence the overall style of the consuming application. The method of access may create levels of build-time and deployment dependencies as well as opportunities that must be considered when a consuming application is selecting the best solution (or if it can be leveraged at all). When creating an API, the designer must keep these characteristics and constraints in mind while considering their primary consumer’s needs.
Methods of Access
Relevant methods of access include:
- Software Library – a collection of software assets used to develop programs accessed as a defined API (specified interfaces) in the host language and connected locally to the program. Libraries may be connected (linked) to programs in different points of the program’s lifecycle. A “static” library will be connected during build of the program while a “dynamic” library will be connected via a loader at runtime as needed by the program via various degrees of sophistication based on the underlying operating system. In both cases, the library API is typically declared via an included code artifact such as header files in the C language.
- Benefits: Good for functional reuse or frameworks with infrequent changes. Libraries are good candidates for implementing the way to communicate (protocol) with a remote resource.
- Implications: Not a good candidate for frequently changing interface signatures (the way to call), functionality or for volatile data. Changes must be distributed to clients that may not have dynamic capabilities, depending upon the platform. As with any API, if the interface signature changes, code impact for consumers will create issues.
- Remote (network) – a collection of software assets used to develop programs accessed on another computing device over a network. Programs can access these components over raw transport/network protocols such as for the internet (TCP/IP), but APIs typically employ techniques to hide this low level of detail such as “remote procedure calls” (RPC) or Message Oriented Middleware (message queuing) frameworks.
- Benefits: Good for frequently changing functionality, process and/or data access.
- Implications: Distributed computing impacts must be considered such as communication transport, machine-to-machine interoperability, varying availability between computers, general communication failures, performance overheads and middleware (infrastructure to support the network transport, protocol handing and routing) requirements.
- Message Oriented Middleware – a refinement of Remote Access Method that creates a virtual communication network of software and hardware components employing the use of intermediary “queues” where request and response payloads can be used to address distributed computing issues such as heterogeneous platforms, network fragility, peer component location management and multi-device availability.
- Benefits: Good for high reliability of frequently changing functionality and data access. MOM addresses and isolates many distributed computing impacts. It moves much of the failure recovery from the consumer to the middleware.
- Implications: Typically requires an asynchronous interaction model that the consumer must support adding more complexity than a synchronous request-wait-for-response model. Must consider performance overheads and significant middleware requirements.
Styles of Organization
Relevant styles of APIs include:
- Procedural – function centric access to routines to complete a specific task. These are typically supported by libraries such as the C language “stdio” library which would contain the fopen() function providing a common interface for opening a file that additional functions can operate by passing a moniker of the open file to each subsequent call. Procedural APIs may also be accessed remotely via RPC implementations.
- Object-oriented – a paradigm for organizing complex applications into “objects” (nouns) that encapsulates data, state and sets of procedures (methods as verbs) operating on that data. Like the Procedural style, OOP is typically supported by libraries with language specific descriptions of the objects expressed as a set of publically accessible attributes (data), methods (procedures/actions) and operations. An example would be the C++ language fstream (based on the iostream set of libraries) in which an object would represent a file, upon which methods and operations can be requested such as fstream::open() and fstream::close(). In this case, a moniker of the file is not required as the object itself encapsulates that in its state. Object-oriented APIs may also be accessed remotely via Object Request Brokers such as implementations based on the Common Object Request Broker (CORBA) standards or Microsoft’s DCOM.
- Service-oriented – a paradigm for exposing distinct activities as services via some protocol where a service is a self-contained representation of an activity, typically in a business context. Service-oriented is similar to OO in that it provides encapsulation with one or more actions (verbs) that can be requested. It differs from OO in that stateful services are avoided. Distributed computing is assumed, so communication is not assumed to be cheap nor transparent, therefore, the concept of protocol is much more prominent which tends to drive higher levels of decoupling and course granularity in design.
- Resource-oriented – a paradigm for organizing software in the form of subjects, or resources, across a consistent (uniform) interface so that the actions are not business-centric, but common to the protocol. The best example of Resource-orientation is the World Wide Web (WWW) in which one may navigate to distinctly addressed (URLs) resources that are presented as pages within a browser. In the case of the WWW, a browser will “GET” a resource to display and “POST” a collected form to update the state of a resource. This deviates from the Service-oriented paradigm in that the focus is on the subject and not an activity, which affects the design of consuming software, but the advantage comes in the ability for a ubiquitous user agent, such as a browser, to interact in a standard fashion.
“Styles” are all related to the model or “paradigm” in which software is organized. In this case, Procedural and Object-oriented (and many others, such as Declarative) are typically related to the paradigm of a single program supported/enforced by language constraints while Service-oriented and Resource-oriented are typically related to the overall paradigm of an architecture of systems and are specifically orthogonal to programming languages, therefore lending to distributed computing.
When it comes to interfaces that are going to be part of compositions created by someone else, we have to consider designs that ensure changes can be isolated to reduce “ripple effects” beyond the interface, interfaces are easily assembled within the composition, interfaces can be reused in many compositions and there are no additional dependencies beyond the interface itself. Meeting these needs will increase use and decrease support/maintenance issues (durability). These needs bring the design notions of “coupling” and “cohesion” into play.
Coupling is our fancy term for measuring the interdependency between components while cohesion is the term used for measuring how well all the elements within a component work together. Based on these needs, we are looking for low/loose coupling with the interface and high/strong cohesion within the component (behind the interface). While this is a significant discussion topic in its own right (such as choosing the right point on the spectrum to install the interface to best accommodate both), the key point is that some styles may better help support these characteristics that others.
Additionally, the API designer must not only consider the paradigm that best fits their model, but also that of their target consumers to determine what will be most aligned with their collective objectives. This alignment is critical in addressing the usability that ensures the API can be used most effectively.
Finally, the consideration of API style is often escalated beyond that of the API designer to those making decisions of enterprise architectures or platforms. In cases where the distribution of function and data is paramount, the overall architectural style must be such that it will readily accommodate consumption of interfaces to those functions and data. This is a very obvious need, but misalignment of styles can make consumption of APIs clumsy at best, avoided at its worse and duplicated in the most tragic of outcomes.
Forward to the Past
In my next post, I will explore the history across the emergence of these types so that we may better understand the circumstances in which they came into existence. My goal is to develop a perspective based on the benefit of hindsight to guide us in our pursuit of a great API.