LitwareHR – Smart Client – Architecture Overview – Part II – Use of LitwareHR On-Line Services


One of the reasons building LitwareHR-SC was fairly easy to do, was because of the Non-Functional On-Line Services. By non-functional services, I mainly mean the metadata API exposed by LitwareHR that allows anybody to introspect into the customized details of a specific tenant instance of the application: entity metadata, views, styles, etc.

For example, the TenantBrandingService uses the GetConfiguredStyle method to retrieve the current CSS style to be applied, and then download the logo for the header workspace:

string cssUrl = presentationService.GetConfiguredStyle(authnService.CurrentTenantAlias);

Even more interesting is the DefSynchronizationService which handles entity and view defintions and synchronization:

For example:

public void SynchronizeDataModelDef()
{
    Dictionary<Guid, string> entityDefList = dataModelService.GetEntityDefList();
    Dictionary<Guid, string>.Enumerator iterator = entityDefList.GetEnumerator();
    while (iterator.MoveNext())
    {
        EntityField[] entityFlds = dataModelService.GetEntityDefFields(authnService.CurrentTenantAlias, iterator.Current.Value);
        SynchronizeEntityDef(iterator.Current.Value.Trim(), entityFlds);
    }
}

 

In this code fragment, DataModelService is the local proxy for these on-line services. The first highlighted method retrieves all entities in LitwareHR, the second retrieves the field descriptions for a given entity (like "position").

This is like a reflection API on LitwareHR that allows a developer to create a generic client on top of it, regardless of the tenant customizations.

LitwareHR-SC is multitenant aware, because it takes into account this requirement. However the application itself is not multitenant in the same way the LitwareHR web client is, because it only serves one user. That's why the local databases is single-tenant and the more complex extensibility mechanisms implemented in the server side are not needed. This is illustrated for example, in the following code segment in the DBService: 

This method creates the local table for a given entity:

private void CreateEntityTable(string entityName, EntityField[] entityFlds)
{
    String createSQL = String.Empty;
    StringBuilder strBld = new StringBuilder();
    strBld.Append(string.Format("CREATE TABLE [{0}] (", entityName));
    strBld.Append("[id] uniqueidentifier, ");

    //Iterate on each field an get the DDL for it
    foreach (EntityField fld in entityFlds)
    {
        strBld.Append(string.Format("[{0}] {1},", fld.Name, GetSQLType(fld.TypeName)));
    }

    //Eliminate the last ","
    createSQL = strBld.ToString(0, strBld.Length - 1);
    createSQL += ")";

    db.ExecuteNonQuery(CommandType.Text, createSQL);
}   

 

Notice that the table created will contain a flat list of fields including those that a Tenant might have created in addition to the stock ones provided out of the box by LitwareHR.

 

Opportunities for improvements:

There are two obvious opportunities for improving LitwareHR-SC, but both require enhancements on the on-line services:

  • Adding a "WhatChanged HasAnythingChanged( Timestamp Since )" method. This would allow to decrease the "chattiness" of the current implementation, replacing all calls querying for each artifact with a single one:

 WhatChanged changes = HasAnythingChanged( lastSynch );

UpdateEntities( changes.EntitiesChangset );

UpdateBranding( changes.BrandingChangeset );

 ...

  • Adding timestamps to entities instances so synchronization of data is more efficient and based on differences. Today we get all instances:

          e.g.:

GetInstanceList(string tenantAlias, string entityName, Guid[] ids, TimeStamp after );

GetInstanceList(string tenantAlias, string entityName, Guid[] ids, TimesSpan within );

 

Skip to main content