Tolerant reader

Embracing the Tolerant Reader Pattern in API Integration


As we continuously improve and evolve our public API to meet the ever-changing requirements and enhance functionality, integrators, and clients must adapt to these changes seamlessly. One pattern that aids in achieving this resilience is the Tolerant Reader.

What is a Tolerant Reader?

The Tolerant Reader pattern is a concept in system integration that suggests when consuming data from another service, the client should be designed to handle possible changes, such as additional fields or minor structure changes, gracefully. This pattern aids in making the integration more resilient and forward-compatible, reducing the need for immediate changes or upgrades from the client side with every minor update in the data or service.

Real-world Scenario

Consider you are calling the GET /organizations/{{id}}. Initially, the response might look like this:

{
    "id": "cc15194a-6bc9-4ebb-b15d-43411a54ba4b",
    "parentOrganizationId": null,
    "legalName": "Empresa Teste Ltda",
    "legalDocument": "86820799000188",
    "metadata": null,
    "status": {
        "code": "ACTIVE",
        "description": null
    },
    "createdAt": "2024-02-08T16:59:31+0300",
    "updatedAt": "2024-02-08T16:59:31+0300",
    "deletedAt": null
}

Later on, we decide to include a doingBusinessAs and address fields to the response:

{
    "id": "cc15194a-6bc9-4ebb-b15d-43411a54ba4b",
    "parentOrganizationId": null,
    "legalName": "Empresa Teste Ltda",
    "doingBusinessAs": "Empresa Teste ",
    "legalDocument": "86820799000188",
    "address": {
        "line1": "Rua Botucatu, 10",
        "line2": "Casa B",
        "zipCode": "04023060",
        "city": "São Paulo",
        "state": "SP",
        "country": "BR"  
    },
    "metadata": null,
    "status": {
        "code": "ACTIVE",
        "description": null
    },
    "createdAt": "2024-02-08T16:59:31+0300",
    "updatedAt": "2024-02-08T16:59:31+0300",
    "deletedAt": null
}

Clients implementing a Tolerant Reader pattern will handle the extra fields (doingBusinessAs and address) gracefully, regardless of whether their current implementation needs it or not.

Implementing a Tolerant Reader

Below is the Python code snippet updated to consider the changes in the payload, specifically handling the additional doingBusinessAs and the nested address object.

def fetch_organization_info(organization_id):
    response = requests.get(f"{host}/organization/{organization_id}")
    org_info = response.json()
    
    # Mandatory fields we expect
    legal_name = org_info.get("legalName", "Unknown")
    legal_document = org_info.get("legalDocument", "No document provided")
    
    # Handling optional fields
    doing_business_as = org_info.get("doingBusinessAs")
    
    # Address is a nested structure which might not be present
    address_info = org_info.get("address", {})
    line1 = address_info.get("line1", "Address not provided")
    city = address_info.get("city", "City not provided")
    state = address_info.get("state", "State not provided")
    country = address_info.get("country", "Country not provided")
    
    # Process the information
    print(f"Legal Name: {legal_name}, Legal Document: {legal_document}")
    print(f"Doing Business As: {doing_business_as}")
    print(f"Address: {line1}, {city}, {state}, {country}")

# Example usage
fetch_organization_info("cc15194a-6bc9-4ebb-b15d-43411a54ba4b")

In this snippet, the get method of the dictionary is used to fetch values. If a new field like phoneNumber is added to the API response, the code will still function correctly by safely accessing the new field without throwing an error if it's absent.

Conclusion

Adopting a Tolerant Reader pattern when integrating with APIs can significantly reduce maintenance effort and improve the system's resilience to changes. It ensures your applications gracefully handle additional information and remain functional without frequent updates, thus providing a smoother integration experience.

Last updated