Designing effective Experience APIs: Principles, Patterns, Anti-patterns.

Hany Elemary
navalia
Published in
9 min readMar 20, 2023

--

Experience APIs are microservices designed around customer journeys as opposed to internal business capabilities.

Imagine, for example, that we’re building a platform like UberEats. “Browsing” nearby restaurants would be a Customer Experience. But, onboarding new restaurants would be a Domain Capability — an internal business concern.

To create delightful experiences for customers, there are best practices that we need to take into account. In this article, we cover Experience APIs’ principles, patterns, anti-patterns and design considerations.

Experience API Principles

Principle 1: Experience APIs should be channel-agnostic.

The API should provide a consistent experience across all touch-points of the customer journey. It should not tailor the content to any specific device or channel. That is the job of a BFF.

Principle 2: Experience APIs should cover a customer journey, not a business capability.

The API should focus on the customer’s end goal. It should help customers achieve their desired outcome. Anything else, like internal business capabilities, are outside their scope.

Principle 3: Experience APIs should have a well-defined boundary.

The API should have a well-defined purpose. It needs to have a clear boundary with a start and an end to the journey. Because customer journeys are often interconnected, the boundary of Experience APIs needs to be designed deliberately.

Principle 4: Experience APIs should be self-sufficient.

The API should encapsulate exactly one experience. It’s a design smell for one Experience API to call another directly. This might be indicative of an insufficient journey boundary. To look at it differently, BFFs shouldn’t call more than a single Experience API to construct one customer journey.

Experience API Patterns and Anti-patterns

Arriving at a sensible service boundary starts with understanding the role and responsibility of any API. Experience APIs can take on different responsibilities and patterns.

The Aggregator Pattern:

Aggregator APIs reach out to other APIs (often in parallel) to stitch their results together and return the result back to their caller.

Experience APIs commonly play the aggregator role. The “Browse” Experience API from earlier may call Domain APIs such as Menu, Restaurant, and Offer. It then aggregates their results and stitch them together in a meaningful way to customers. Here is a diagram to illustrate this flow:

Browse Experience API calls Domain APIs and aggregates results

Anti-pattern: Anemic Services

Anemic services are void of meaningful behavior and rules. They may seem like legitimate experiences at first. But, in reality, they are simple features masquerading as a bounded customer journey. Here is how to recognize this anti-pattern:

  • More logic and processing on the client side. This often needs duplication across other channels.
  • An Experience API calling another to form a single Experience. This leads to unnecessary chatter between services.

While the Aggregator pattern is common, it may bring the tendency to build anemic services.

For instance, one could inaccurately think of the “Browse” Experience API as a single end-point merely wrangling data together. But this will incur the overhead of a new microservice with limited benefits. Part of the Experience should include capabilities for searching, filtering and sorting based on many attributes such as: Price Range, Ratings, Dietary restrictions, Delivery time, etc.

These filtering and sorting capabilities may also need to be dependent on Order Type (Delivery, Dine-in, Pick-up). For instance, we may not need to show “Delivery time” as a filter/sort capability if the customer is trying to order Pick-up. This shouldn’t be left to client-side logic.

If in doubt on whether to build an Experience API with shallow functionality, it’s best to wait. When a journey naturally emerges down the road, harvest it into an Experience API in the architecture.

The Orchestrator Pattern:

Orchestrator APIs coordinate other API calls (often in sequence) to the rest of the system. They are also responsible for unwinding calls should things go wrong.

Experience APIs may orchestrate calls to different services in some scenarios. For instance, a Shopping Bag Experience API allows customers to:

  • Add, remove and update items in the bag
  • View recommendations based on past orders.
  • Checkout the bag to get delicious food

In the checkout scenario, we may notice the following sequences to our journey:

  1. Apply customer offers to the bag.
  2. Apply existing customer reward points to the bag.
  3. Calculate total price with state taxes and delivery fees.

Here is the corresponding diagram:

Shopping Bag Experience API sequentially calling services during the checkout step

Anti-pattern: Tightly-coupled Services

While orchestration is common in microservices, it can lead to tight coupling.

A common pitfall here is that orchestrator services can become a tangled mess of calls over time. Without careful consideration, we end up with “what’s another service call” implementation. Over time, orchestrator services become coupled to more services. This hampers the ability to decouple the architecture later.

Orchestrator services need to be bounded around the calls they make to other services. Notice, for example, that orchestration in the Shopping Bag happens at the end of the Experience; checkout. This is a deliberate decision that highlights where the seams are for the Experience API. A hand-off point, if you will.

The Event Collaborator¹ Pattern:

Event Collaborator APIs coordinate communication with other APIs by listening and responding to events. They coordinate communication without needing to know of other APIs. They only know about events of interest.

Experience APIs can take part in event collaboration to manage their own state and validations. This allows them to be independent without knowing about other services. Minimal dependencies means loose coupling, which means independent deployment — the target for microservices architecture.

Let’s take a look at an example. After customers place orders, their reward points are accrued. This happens via Domain APIs such as Order Management API and Rewards API. These orders are now part of customers’ Past Orders, an Experience API, so they may re-order them again in the future.

Here is a diagram of how events can construct the state of the Past Orders Experience API.

Event Choreography for Past Orders Experience API

Here’s the flow:

  1. Order placed — Order Placed Event.
  2. Reward points updated and broadcasted back.
  3. The customer Past Orders updated with order content and reward points.

This flow happened without services knowing about each other’s existence. They only needed to know about the event Orders Topic.

In this case, we’ve separated the business rules from the customer experience. The business rules here encompass how the Order transitions from Submitted to Fulfilled. The customer experience is being able to view, search, download/email receipts, re-order, favorite and rate Past Orders.

But we’ve introduced data duplication by going this route. Order data is now duplicated in Order Management and Past Orders APIs. While this can be a sticking point for some, it’s a better alternative since it reduces coupling between services. Disk space is cheap and cloud storage services are a dime a dozen.

This duplication also gives finer control over data models. Now we can store and retrieve data in the most optimal way. As an example, we could serve data more quickly by caching the top 5 most recent orders for each customer.

Caching the top 5 most recent orders for robust access

Anti-pattern: Events-only Microservices

While there is tremendous value in pure event-driven systems, they introduce challenges. One of the challenges is that they aren’t always linear. They happen asynchronously. This makes them more complex to debug as the number of events increases. The response can vary depending on the event’s timing, order, and context.

These challenges need advanced expertise in testing systems effectively. Writing automated tests that cover all possible scenarios and edge cases can be challenging. For one, events can happen out of order. And, two, the attention shifts away from ensuring other services were called properly to ensuring certain events happened in the right sequence. While not difficult per se, it requires a mental shift and deliberate practice to be proficient.

Depending on the context, a combination of event-driven communication and orchestration can produce a flexible, cost-efficient, and easy-to-implement system.

A Hybrid Pattern:

Experience APIs can also play more than a single role within the constraints of their boundary.

Consider the example of Shopping Bag Experience API discussed before. Besides being an orchestrator, the Shopping Bag API keeps track of different customer bags as state. This enables stickiness across channels. Customers can start a bag on a web browser and pick up where they left off on a mobile device.

Personalized content and offers are another benefit here. They can persist regardless of the channel or device customers are using. This creates a sense of continuity and familiarity for customers. And, as a result, it improves overall customer satisfaction and loyalty.

Without this Experience API, this seamless movement across channels will either be missing or channels will have to constantly sync their state from other APIs.

Anti-pattern: Bloated Services

Bloated services become excessively complex or overloaded with unnecessary features. Experience APIs on this hybrid pattern may bring tendencies to cram more functionality into the service. Because they can have many responsibilities, they are often coupled to too many dependencies. Any minor sign of clutter will invite more clutter. This eventually leads to services getting bloated over time.

Ensuring that the experience is self-sufficient and bounded can help avoid this anti-pattern. It’s critical to maintain a clear understanding of the service’s purpose and only include features that align with that purpose.

Design Considerations

Latency

A common concern with Orchestrator or Aggregator Experience APIs is that they add latency. This is a fair concern. It needs evaluation on a case-by-case basis. That said, the benefits of shareable Experience APIs usually trump the minimal latency they may add. If we find that latency has a significant performance impact, here are some routes to explore:

  • Caching data
  • Serving requests via a CDN
  • Compressing responses
  • Using binary data exchange

Security

Designing any API requires thinking about its accessibility and security. Is it Public, Internal or Private? Does it handle sensitive financial data (PCI), personal or medical info (PII or EMR)?All these questions inform security design in the architecture.

Separating Experience APIs from Domain APIs adds layered security in the architecture. Since Experience APIs surface customer journeys across different channels, they can either be public or internal depending on the sensitivity of the data they process.

Domain APIs can be internal with no public access. Meaning, only services within the platform can call them. Other Domain APIs may need stricter rules. For instance, if they handle sensitive data (i.e: PCI), they may need to be private. This means that only an allowed list of services can call them. Here is a diagram to illustrate this security segmentation:

Finer security segmentation based on the API responsibility

Closing Thoughts

  • Experience APIs focus on customer journeys while being channel-agnostic, cohesive and self-sufficient.
  • Experience APIs can play a variety of roles in the architecture. Aggregator, orchestrator, event collaborator, and hybrid patterns are common patterns of building Experience APIs
  • There are design anti-patterns with each role an Experience API plays. Most of the anti-patterns stem from not understanding the right service boundary.
  • Experience APIs trade off latency for architecture flexibility and layered security.
  • Unless the customer journey is indisputably obvious, harvesting Experience APIs later is recommended.

Acknowledgments

Big thanks to Smitha Ajay, Luke Belliveau, Elliott Branecky, Bruna Castelo, Megan Lusher, Cliff Morehead, Lav Pathak, Gedeon Santos, Ibrahim Taha and Yujie Wu for providing feedback on this article.

Additional reads

Footnotes

¹ The Event Collaborator pattern is also known as Event Choreography.

--

--

Hany Elemary
navalia

Technology Leader. High Performing Teams Enabler. Author & Speaker.