Compute Services

Rapidly developing applications (part 6): designing and versioning APIs

Share this post:

In this 7-part series on microservices application development, we provide a context for defining a cloud-based pilot project or minimum viable product that best fits current needs of your team and organization, and prepares for a longer-term cloud adoption decision.

Here in part 6, we review best practices for creating and maintaining APIs within an application and between a cloud-deployed application and components that live on premises.

This is a guide to the overall series:

  • An overview of microservices (part 1), after providing context for business pressures requiring faster app development and delivery, steps through the process a team went through in transforming a specific monolithic application.
  • Architecting with microservices (part 2) lays out the common capabilities of an architecture for rapidly developing applications based on a microservice.
  • Implementing a microservices application (part 3) provides a method for implementing your own microservices project.
  • Using microservices development patterns (part 4) presents common development patterns available for implementing microservices applications.
  • Using microservices operations patterns for resiliency (part 5) presents common operations patterns for achieving resiliency in your microservices applications.
  • Designing and versioning APIs (this part) offers best practices for managing the interfaces of microservices in your application.
  • Securing your microservices (part 7) covers the common tactics of managing access to microservices.

As you review the options and best practices, keep in mind this case study:

Creating an API for a microservice

These are basic principles for designing the API exposed by a microservice:

Enforce strong contracts

A microservice as such provides a versioned, well-defined contract to its clients, which are other microservices. Each service must not break these versioned contracts until it’s known that no other microservice relies on a particular, versioned contract.

Avoid chatty interfaces

Chatty interfaces require you to perform multiple calls to accomplish a task. Inefficiency in communications within a distributed system inevitably impair service performance and availability as a microservices application scales.

Make a message serialization choice based on performance

Who are the users of the format, how much data is being transferred in each request, can the data be compressed? Though JSON is currently the popular format for microservices APIs, and can be parsed directly into an object graph, it’s not a compact format. As an API designer, evaluate formats and choose one based on performance requirements.

Use desired resiliency as the guide for choosing blocking vs. non-blocking APIs

One of the most important aspects of API design is whether to use blocking or non-blocking calls. Non-blocking APIs scale better, but are more complicated to design and use. Blocking APIs allow for retrying when the resource becomes available. Deciding which type to use where depends on how resilient the API must be within the application workflow as it scales.

Using API design patterns

Here are basic design patterns that inform the API design of microservices.

API Gateway Pattern

An API Gateway is used to abstract the communication between client applications and internal microservices. The API Gateway allows for the composition of microservices into client-ready services.

 

 

 

 

 

 

 

 

Microservices Discovery Pattern

The Microservices Discovery Pattern removes coupling between microservices and client apps.

By dynamically registering microservices in an enterprise topology, we allow client applications and other services to dynamically discover microservices and adapt to changes. This pattern also avoids the centralized registry pattern of traditional SOA monoliths.

 

 

 

 

 

 

 

 

 

 

Microservices Description Pattern

The Microservices Description Pattern expresses features of microservices in a descriptive format that can be understood by client applications. It also offers a means of managing microservices metadata.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Versioning Microservices

There are two options for versioning the exposed API of a microservice. If you need to provide additional information on a GET or POST operation, then the change is unlikely to be backwards-compatible. In that case, you need to look at ways of handling this problem. The two most common ways of handling this are:

  1. Versioning in the URI
  2. Versioning in the header

The REST community is split nearly 50/50 on which is the best approach for this, so we present both.

URI versioning

Versioning in the URI is when you change the URI of the resource itself to contain version information. A simple example of this from our bank account example might look like:  /accounts/v2.1/{id}

This approach gives you the ability to version an entire resource hierarchy or branch. It’s also more semantically meaningful to developers—they can see at a glance which version of a service they are referring to. Modeling the version in this way as a resource enables automated navigation or discovery of resources. For this reason, we recommend it for most purposes.

A disadvantage of this approach is that when you include version information in the URI, you change the resource name and location. This introduces a complex proliferation of URI aliases that make it difficult to identify which version of your API is the currently supported version. What’s more, you can no longer use URIs to compare identity in this approach — the same identical object may be returned by both the version 2.0 and 2.1 URI’s.

Additionally, this may break existing hypermedia links that do not include version information.

You can get fancier with this approach, but this makes it troublesome as the examples show:

versioning at multiple hierarchy nodes – complicated

/maps/version/2/roadways/version/2

query parameter – not recommended

/maps?version=2

Impact to the backend

URI service versioning is the best practice for updating the public API of a service. But putting URI versioning in place doesn’t address any breaking changes to the backend data stores that may need to take place. There are two options for dealing with this, and neither option is great:

Option 1: Copy your old data into a new “V2” database and keep the two entirely separate. This means that either you live with data drift or you put a data synchronization solution in place.

Option 2: Update your schema in place and add code to v1 (!) to handle the new schema.

The following image shows these two options:

 

Header versioning

Another approach is to include version information in a special header of each request or response. An example of this header might be: X-Version:2.1

An advantage of this approach is that the resource name and location remains unchanged throughout your hierarchy, so you won’t have a proliferation of URI aliases. This approach makes it easier for transparent intermediaries to parse the headers for routing in scenarios where you have an ESB in place between service requestors and service providers. Likewise, by keeping the URI the same across versions, the API remains completely semantically meaningful to developers.

A drawback to this type of versioning is that information can’t be readily encoded into hypermedia links. What’s more, this approach doesn’t discriminate among multiple representations. Additionally, it only works with custom clients that know how to encode the special header, thus introducing coupling into your design.

What to do from here:

Add Comment
No Comments

Leave a Reply

Your email address will not be published.Required fields are marked *

More How-tos Stories

WebSphere on the Cloud: Application Modernization (Phase 1)

In our last post, we took high-level view of our four-phase process for moving WebSphere applications to the cloud. In this entry, we'll look closely at the tools that help you with Phase 1, modernizing your existing WebSphere application.

Continue reading

Taking WebSphere Applications to the Cloud

With many traditional workloads moving to cloud-based infrastructures, now is the perfect time to assess your existing Java-based workloads and migrate them to WebSphere Application Server on IBM Bluemix. More commonly known as “WAS on Cloud”, this service provides a fully-configured, turn-key WebSphere-based application environment, supporting both Traditional WAS and Liberty applications. You use your same tried & true wsadmin automation scripts, same CI/CD techniques, but just point them at Bluemix instead of your own infrastructure! This post summarizes the steps from WAS to the cloud.

Continue reading

Introducing the IBM Developer Extension for Visual Studio Code

We're delighted to introduce to you the IBM Developer Extension for Visual Studio code. This extension provides access to capabilities from the IBM developer CLI directly within the Visual Studio Code editor's command palette. It enables you to quickly access a subset of bx dev commands for both Docker and CloudFoundry workflows, including app deployment, starting/stoping/restarting apps on Bluemix, viewing remote app logs, and more - all without the need to leave the editor's context.

Continue reading