However, if your service design is good, but the performance of the service is bad or it cannot be evolved fast enough to meet changing market conditions then your API will fail to be useful.
In this blog post, I want to extend the conversation from the design of the API contract to the architecture of the target endpoint (the source application that executes the API behaviors).
The example that I am using in this article is a continuation of the How to shift your APIs perspective from Systems to Consumers story. The design of the hierarchy API that my customer designed was based on a system that was built to efficiently manage many hierarchy types in a single, extensible system. This is a great systems design, but it creates some challenges when it comes to the ability to independently evolve one hierarchy type without impacting all systems that use the other hierarchy types. It also has challenges with comprehensibility due to the complete abstraction necessary to manage many hierarchies in a single schema.
The following is an incremental approach to gaining autonomy and speed-to-market for application evolution.
The current state of the application is designed as a single system for many hierarchies. The interface to the application gives the ability to pull any hierarchy based on a top-level hierarchy code. A single application and database store all of the hierarchies and metadata. The database can represent many types of hierarchies: Sales Hierarchy, Marketing Hierarchy, Operation Hierarchy and more.
Our current state looks something like this:
Step 1: API Gateway
As a quick first step, we can gain the benefit of a self-evident path structure and payload structure by surfacing three distinct APIs in the API Gateway using simple transformation. We would create three distinct URLs from in the gateway to represent the Sales Hierarchy, Marketing Hierarchy and Operation Hierarchy and alter the payloads to explicitly represent each of the new resource types. To significantly improve performance, we can now evaluate the ability to cache the requests at the API gateway for each API endpoint.
We will essentially swap the dumb reverse proxy for an API Gateway. The new environment would look something like this:
- Security (Authentication/Authorization) is now done in a uniform fashion.
- Monitoring of all API traffic can be done from a single point.
- Understanding of APIs for consuming developers is now simple and self-evident.
- The Gateway allows us to quickly create payload transformations to create the APIs.
- If API Gateway cache can be used we will gain significant performance improvements.
- By doing this radical transformation at the API Gateway you are adding a good amount of business logic to the Gateway. This will broaden the net of considerations for when you make changes to the API and create more complexity.
- Large amounts of payload transformation in the API Gateway can create performance (response time) concerns.
Step 2: API Façades
The next step is a little more ideal. It moves the business logic into an API Façade layer just below the API Gateway. We would move the Sales Hierarchy, Marketing Hierarchy and Operations Hierarchy into their own application layers. This will remove the logic from the gateway and create an opportunity to supplement and change the logic for the presentation of any individual hierarchy to meet market demand without being restricted by the other hierarchies needs.
That architecture would look more like this:
- Team autonomy.
- Architecture autonomy. This design will allow the architecture for the façade to differ for each service without impacting the other services.
- Code freedom. The façade will not be bound by the execution limitations of your API gateway.
- All of the APIs still share the base hierarchy application and database. Therefore, limiting their ability to pivot and evolve based on their consumer demand without impacting the other services or being hindering time to market.
- Stack complexity. We have added a new layer and more complexity to the stack. We need to ensure that the benefit of autonomy and comprehensibility value are significant enough to overcome this complexity cost.
Step 3: Autonomy
The most ideal state is for the APIs to sit atop of autonomous application stacks. Each of our hierarchies (Sales, Marketing and Operations) would live in their own application stack. The logical first step would be to replicate the code and database and just separate them onto independent instances. By separating them, you will allow for the database and the code to be evolved independently as it is demanded by the consumer use cases. This will create autonomy and simplicity that will make it easier for new teammates to understand the structures and purpose of the code and database. Performance gains can be realized by denormalizing databases and storing the data in nosql or data cache that will allow for highly performant data reads.
- A single team or two teams that work closely together serving the same consumer demand can now own the entire stack.
- The entire application architecture can now be elastically designed for each use case independent of the other service requirements or demands.
- Speed to market can be improved by moving small changes for the specific application through the DevOps process and into production quickly.
- This approach creates a more complex infrastructure landscape. However if you are working with platform technologies (serverless or containerized architectures) that are intended to be provided to teams as services, this challenge will be overcome with volume.
- Shifting from a legacy centralized data model that has direct consumers can prove an organizational challenge.
Great APIs solve consumer problems and are easy to understand and use. However, if your API fails to perform or evolve with your marketplace then it will cease to be valuable. Where you need high performance or rapid evolution, consider API Gateway, microservices and CQRS patterns that will increase performance and accelerate delivery.