If you look at the Tenant entity, there is an “Is_Active” attribute on it. Setting this to false has these very important ramifications:
- Multi-tenant Timers will not run for this tenant
- Processes will not run for this tenant
- Users cannot login for this tenant
These are all very sensible default behaviors. However, in a multi-tenant application, these are not always the best behaviors.
Consider a SaaS application where multi-tenant Timers may run for a number of reasons, like calculating reports daily or billing the tenant’s clients once a night. In addition, Timers (usually with a schedule of “When Published”) are often used in applications during a deployment to adjust data to the latest version of the code (for example, a new default value is being set for an Attribute that previously was not mandatory, or moving data from one Entity to another to support a refactoring, etc. etc. etc.). In this article, I will call these “data migration Timers”.
So far so good!
Further consider that in a SaaS application, there are times where you need to disable to tenant in terms of its daily use (prevent users from logging in, not run nightly timers to bill the tenant’s clients, and so on). But… and this is a big “but”… you expect that you may need to re-activate the tenant in the future. Perhaps the tenant has requested a one-month suspension because they are in the middle of moving offices, or maybe they have not paid their invoice to you but have not cancelled service. Maybe even your policy is to allow a customer to return at any given time in the future and have their account returned to exactly where it was when they cancelled service.
You clearly do not want to allow users to login to this tenant, you want to hide it in most lists of tenants, and the daily operational Timers should definitely not run for this tenant. However, you still want your data migration Timers to run.
Just setting Tenant.Is_Active to false will not produce the desired results. You end up either needing to retain, maintain, and test those data migration Timers forever, which is “chaotic” to say the least (you’ll also be stuck keeping the old data models forever!), or be in a situation where a re-activated tenant’s data simply does not work at all in the new system… or worse, looks OK but is inconsistent.
What does work very well is the following:
- Create an extension Entity to Tenant (I usually call it TenantExtended), if you do not have one already. I tend to set the Id to type Tenant Identifier. This Entity should be a single-tenant Entity!
- Add to TenantExtended a Boolean Attribute called “IsActive”.
- While here, I like to add a “PublicName” Attribute, since the names allowed in Tenant.Name have restrictions on things like space characters in them. Let’s also give it a unique index.
- In the properties of TenantExtended, set its “Is Active” Attribute to “IsActive”. This will make sure that whenever you make a list on this Entity, it automatically filters on TenantExtended.IsActive. If you added a PublicName Attribute make it the “Label” Attribute for the Entity as well.
Now that the Entity is properly configured, let’s make the needed logic changes:
- In your logic to de-activate a Tenant (or create an Action for this if you do not have it already) change it from setting Tenant.Is_Active to setting TenantExtended.IsActive.
- Create a Tenant_GetIsActive Action that checks TenantExtended.IsActive. Make it a function. This will be a handy shortcut to use in the future. For example, in the next step…
- In your typical multi-tenant operation Timers, start with a call to Tenant_GetIsActive() to see if they should actually run or not. Do not do this check in the data migration Timers.
- In your Login logic, make a check for Tenant_GetIsActive() to make sure that their user is in an active Tenant before calling Login (from System) or User_Login (from Users).
- If needed, add an Attribute to TenantExtended to mark a tenant for deletion, and have a regular Timer (deleting lots of data can be time consuming, so best to do it on a Timer) look for those tenants and completely remove all data for them.
Following this pattern will give your multi-tenant application the flexibility it needs to allow you to suspend a tenant’s access while still allowing them to re-activate the account in the future with no issues.