Tuesday, November 1, 2022

The multi-tenant Frontend case study

 This part of my upcoming book delves into an example of writing an Angular Application with multi-tenancy. One of the very common asks is for an application to be made available in a different appearance for another business in the parent organization’s portfolio. Occasionally, this business might have its own active directory domain and missing federation.

The choice of technology stack only affects the development of the software since multi-tenancy is an architectural decision. If they were different applications for each business, they could implement independent technology stacks regardless of whether the data provider services, and store are common to the businesses. When there is a user interface involved, the technology stack leans towards write-once-run-anywhere paradigm. Angular JS fits this bill very well. It can be developed across all platforms – mobile, web, native mobile and native desktop. Web workers and server-side rendering makes it super-fast and offloads the data model building to push-model frameworks like Immutable.js. Finally, Angular is developer friendly in that it has simple declarative templates and the language for templates can be extended. This makes it easy to get functionality right out of the box and spend less time making code work. Google uses Angular for some of its largest applications.

The switch between single tenancy to multi-tenancy is handled by a few components within an Angular application. First, there is a request interceptor that sets the appropriate tenant in the context. Second, this must be communicated to the server part of the application. Finally, the tenant-based customizations, automatic injection of services specific to the tenant domain, and reconfiguration of routes and redirects as per the domain of the tenant and a few other features must be enabled.

The URLs for the tenant-specific domain name hosted services usually follow a pattern that can be made well-known across the application code base so that the same code works for every tenant. If the pattern and the whitelisting must be avoided, the tenant information can be persisted and looked up as an alternative from a tenant registry. Angular defines a module and a service so these are added to the service.

Then there will be methods provided for each of the following in the service: translation of the hostname/domain name to the tenant, adding a tenant information specifying header to the set of headers on the incoming request and defining enumerations of the tenants if the resolution is based on pattern and literal matching.

The server side of the application will resolve multi-tenancy and inject different services based on the resolution. The interceptor serves this purpose. It must be registered with the module. The service itself, say one dedicated to logins, will have separate instances for the tenant so that they can target different membership providers like the Active Directory. There can be some efficiency with a base class and deriving the independent service implementations. Angular applications require that this be defined in module and within the module, when the provider is configured, a factory can be configured that gets the correct instance of the service.

The login service cannot typically be injected directly as an @Injectable but the use of a factory allows us to refer to the correct instance of the service. The module definition must be updated with the login module. Lastly, there must be switching between the routes based on the tenant service in the login module. 

No comments:

Post a Comment