Filter by Brand Clear Filter

Managing and storing international restaurant addresses in Salesforce

06 September 2018

As mentioned in the previous article, Salesforce is a core platform at Delivery Hero to manage and acquire partnering restaurants. Since we are operating across the globe in more than 40 countries, we are facing various challenges especially concerning the restaurant location in Salesforce.

This article focuses on the seemingly simple problem of entering and storing restaurant addresses. As you might know, addresses and locations are communicated all over the world in different ways. While in some countries like Colombia you assign numbers to street names and you might receive an address that looks more like a code (Kr 72 no. 14-28), in other countries like the United Arab Emirates or Saudi Arabia street addresses or area codes are not used at all.Hence, these are the major challenges that need to be addressed by our Salesforce Team:

  • Manage different types of addresses for each restaurant such as billing address, shipping address and physical location of the restaurant
  • Implement one enterprise solution that extends to all Delivery Hero entities. Most western countries use the Google Places API to locate the restaurants on their respective websites, while other countries (in the Middle East or Asia) will rely on different APIs or entirely custom address models
  • Allow sales representatives to locate exactly where the restaurant is on a map by specifying its latitude and longitude
  • Prevent manual typo mistakes by providing an autocomplete feature or picklist field in the form.

Architecture overview

To meet all requirements from different countries, we decided to build two different address models:

  • GPA (Google Places API) Model: used by all countries that support Google Places API
  • xAL (eXtensible Address Language) Model: based on the OASIS Committees’ extensible Address Language used by those countries that rely on custom address models.

Address data is stored in a custom object (Address Details) related to Account in Master-Detail. Each model will have his own set of fields, along with a generic set of fields to provide a comment interface for other dependent systems, hiding the complexity of each different model. Every address record can be a different type (i.e Billing, Shipping, Restaurant) concurrently in order to reduce the number of records while saving data storage capacity.

xAL Model architecture

First, a brief introduction to the OASIS Committee’s extensible Address Language detailing why it was created, how it works and how we are using it.

Quoting the OASIS Committee:
“Fitting over 200 countries into a unified format is no easy task. Countries have very different address formats. Some use street names for addressing, others don’t. Some use island names, others don’t. The format must allow for all these different types of addresses while at the same time provide a consistent and easy to use format. There are different ways to model data, including hierarchical, relational and object-oriented. Address data is hierarchical in nature (a country has cities, a city has streets and a street has premises) so a hierarchical model is the most natural fit.”

xAL model’s ultimate purpose is to define a xml schema that allows us to describe an address in all its components. It’s a hierarchical model where the root of every item is the country’s origin of that address. All other elements are not mandatory, and can (should) be excluded if not needed. The following diagram illustrates the xAL elements we use and how they relate to each other:

In Salesforce, the model is stored using Custom Metadata Types. Every Custom Metadata Type record represents a value that a sales representative can easily choose from a picklist while creating a new address. After saving the form, the values are copied to a new Address Detail record. There are three main advantages of using Custom Metadata Types over storing the model with custom objects:

  • less amount of data storage required. The amount of storage needed by a custom metadata record is dependent on the number of fields the custom metadata type has, while a custom object is fixed to 2KB
  • custom metadata types support relationships between different types, making SOQL queries fast and efficient when retrieving data. This also doesn’t affect the governor limits on SOQL queries
  • custom metadata records can be easily versioned in GIT, then deployed using the ANT migration tool.

This could be an example for Germany that follows our current model:

As you can see, the model does not require that all addresses are defined with the same structure. For example, in Berlin we want to specify the district in which the restaurant is located, while in Munich we are fine with just the postal code.

GPA Model architecture

Google Places API is a paid service that provides information about places, including address components and geolocation data. It also offers an autocomplete feature, which when used in conjunction with an HTML form, makes it our primary choice when deciding which address model to rollout to any given country. Unfortunately, some countries still do not support the Google Places API.

In Salesforce, we will expose an HTML form with the autocomplete feature enabled, which allows sales representatives to search for addresses or even by specific restaurant (by restaurant name for example). Once the sales representative selects the correct address from the suggestion proposed, Google Places API service can provide all the necessary information regarding the place, which can then be stored in to the Address Detail object record by parsing the response.

Here is a partial example of a GPA response (included the fields that matters to address the us):

[
 {
   "address_components": [
     {
       "long_name": "179",
       "short_name": "179",
       "types": [
         "street_number"
       ]
     },
     {
       "long_name": "Torstraße",
       "short_name": "Torstraße",
       "types": [
         "route"
       ]
     },
     {
       "long_name": "Mitte",
       "short_name": "Mitte",
       "types": [
         "sublocality_level_1",
         "sublocality",
         "political"
       ]
     },
     {
       "long_name": "Berlin",
       "short_name": "Berlin",
       "types": [
         "locality",
         "political"
       ]
     },
     {
       "long_name": "Berlin",
       "short_name": "Berlin",
       "types": [
         "administrative_area_level_1",
         "political"
       ]
     },
     {
       "long_name": "Germany",
       "short_name": "DE",
       "types": [
         "country",
         "political"
       ]
     },
     {
       "long_name": "10115",
       "short_name": "10115",
       "types": [
         "postal_code"
       ]
     }
   ],
   "formatted_address": "Torstraße 179, 10115 Berlin, Germany",
   "geometry": {
     "location": {},
     "viewport": {
       "f": {
         "b": 52.5273070197085,
         "f": 52.5300049802915
       },
       "b": {
         "b": 13.392627669708531,
         "f": 13.395325630291495
       }
     }
   },
   "place_id": "ChIJ8eiBRe9RqEcRJvEypGEm1ok"
 }
]
  • address_components: is the property holding of the different address’ components (e.g. street name, street number, city, …). Each item has two properties:
  • long_name: usually contains a value intended to be presented to a user. It could be different according to browser’s locale;
  • short_name: usually contains a constant value across different locales. Should be used for integration purposes;

To identify what each item of the address_components property represents, we have to examine the types property of each one. In the example above, we can see that the item with type street_number is clearly holding the street number value for this specific address, the one with type route is holding the street name information, the one with postal_code is holding the postal code and so on.Unfortunately, there is no clear documentation on how a specific type is assigned to each address’ component, but it seems to be consistent at country level. For example in a UK address,the component holding the city name will have a type postal_town, while in Germany it will have a type locality.

In order to parse all different type of components, we created a custom setting that will hold, dependent on the country, the mapping between a type and the correspondent field on Address Detail record within Salesforce. More details on this can be found in the implementation paragraph below. Using the Google Places API gives the advantage of getting the coordinates of a restaurant without any additional action by the sales agent, which is not possible through xAL model. For this reason the GPA model must always be preferred over the xAL.

Model Implementation

Below is a detailed explanation on how the GPA model works in Salesforce and how we store the information.

Custom settings

As mentioned before, there is no clear documentation that explains how a certain type is assigned to each address_components item. The only way to determine is by examining some actual responses. In order to be as flexible as possible, we store the components we want to present to the user in the form, their position in a grid (with maximum two columns) and the mapping with the type values returned by the API in a custom setting.

These are the configuration we have for Germany:

The final UI looks like this:

This case described is just the tip of the iceberg. On the one hand we need to be able to apply it globally, while we also need to be able to adjust it to the local needs and conditions as much as possible. Staying flexible to the needs of our business is a great and exciting challenge.

Does this sound like an interesting challenge you would like to work on as well?

If so, check out our Careersite or join our Hero Talent Community to receive updates about upcoming events, interesting articles, and potential jobs based on your interests!