Broadleaf Microservices

Tenant & Application Walkthrough

In this walkthrough, we’ll be examining how we handle resolving the tenant and application along with setting up the TenantContext for the downstream components. This walkthrough is mostly about the API calls since this occurs before we start rendering most components. Also, take note that this assumes the app is not rendered server-side and must make requests on initial page load to the Tenant Services to resolve the tenant information.

What is Tenancy?

Before we can load any content, we need to establish the TenantContext. This informs all API requests about the current Tenant and Application, which are used for narrowing the results of API requests to Broadleaf Microservices on the backend. "Tenancy" represents the ability to create hierarchies in our data and silo off portions of it for specific request contexts.

At the top, is the "Tenant". The Tenant represents a distinct, independent operator of the Broadleaf services. Tenants share the same infrastructure (e.g., services) but do not share data (e.g., catalogs, products, assets). Thus, a single instance of Broadleaf could be shared by multiple companies (i.e., Tenants) without allowing their data to be mixed together.

Beneath a Tenant reside its "Applications". A single Tenant can maintain multiple applications. An Application represents a storefront or commerce-facing frontend. All Applications have an identifier that defines part or all of a URL they can be resolved at. When resolution occurs, an Application can be identified by the domain, domain prefix, or a query parameter in the request’s URL. For example:

  • Domain: www.my-application.com

  • Domain Prefix: my-application.my-company.com

  • Query Parameter: www.my-company.com?application=my-application

Catalogs, menus, and assets, then, become associated with Applications, thus tying those entities to a URL for commerce purposes.

Note
Tenants can also have associated URL components, but these are used for resolving the Admin app rather than the commerce one.

Tenants and Applications also contain important configuration details for frontends such as:

  • What locales their content supports for localization

  • What currencies they support

  • The default locale to localize content for

  • The default currency for the same

  • A reference to the logo to render, including its metadata such as the alt-text, title, and tags.

How does Tenant resolution work?

When a user goes to a URL in their browser that maps to the app we’re running, the app first sends a request to the Tenant Resolver Endpoint This endpoint takes a single parameter—url—which should be the browser’s current location. The endpoint matches url against the Applications' identifiers.

After identifying the Application, the endpoint then fetches the parent Tenant and returns both. The commerce app then sets up the TenantContext using this data. It will also cache the results of the request so that it doesn’t have to resolve it over and over again for subsequent requests as the users navigate the site.

Tip
Once the Application is resolved, the LocaleContext will also be updated based on the default and allowed locales present.
Request to match against the domain

GET /api/tenant/resolver/application?url=https://www.my-application.com/

Request to match against the domain prefix

GET /api/tenant/resolver/application?url=https://my-application.my-company.com/

Request to match against a parameter

GET /api/tenant/resolver/application?url=https://www.my-company.com/?application=my-application

Example 1. Response Payload
{
  "tenant": {
    "id": "string",
    "name": "string",
    "identifierType": "DOMAIN",
    "identifierValue": "string",
    "defaultLocale": "en_US",
    "allowedLocales": [
      "en",
      "en_US"
    ],
    "logoAsset": {
      "applicationId": "string",
      "tenantId": "string",
      "type": "string",
      "provider": "BROADLEAF",
      "url": "string",
      "contentUrl": "string",
      "embedCode": "string",
      "altText": "string",
      "title": "string",
      "tags": [
        "string"
      ]
    },
    "attributes": {}
  },
  "application": {
    "id": "string",
    "name": "My Application",
    "identifierType": "DOMAIN|DOMAIN_PREFIX|PARAMETER",
    "identifierValue": "www.my-application.com|my-application|my-application",
    "customerContextId": "string",
    "deactivated": false,
    "defaultLocale": "en",
    "allowedLocales": [],
    "logoAsset": {
      "applicationId": "string",
      "tenantId": "string",
      "type": "string",
      "provider": "BROADLEAF",
      "url": "string",
      "contentUrl": "string",
      "embedCode": "string",
      "altText": "string",
      "title": "string",
      "tags": [
        "string"
      ]
    },
    "portraitAsset": {
      "applicationId": "string",
      "tenantId": "string",
      "type": "string",
      "provider": "BROADLEAF",
      "url": "string",
      "contentUrl": "string",
      "embedCode": "string",
      "altText": "string",
      "title": "string",
      "tags": [
        "string"
      ]
    },
    "attributes": {
      "additionalProp1": {}
    },
    "isolatedCatalogs": [],
    "contextState": {
      "mutable": true,
      "fieldChanges": [
        {
          "fieldName": "string",
          "sandboxId": "string",
          "sandboxName": "string",
          "stage": "string",
          "author": "string",
          "changeType": "string",
          "timestamp": "2020-11-20T22:06:54.927Z",
          "level": 1
        }
      ],
      "level": 1,
      "catalog": {
        "contextId": "string",
        "name": "string",
        "locale": "string"
      },
      "sandbox": "string",
      "application": {
        "contextId": "string",
        "name": "string",
        "defaultLocale": "string"
      },
      "tenant": "string",
      "customerContextId": "string"
    }
  }
}

What if no Application is resolved?

It’s up to the implementor to decide how to handle this. It may make sense to define a "default" Application to always fallback to. Out of the box, the starter app just renders a 404 not found page.

Application Not Found