Blog Post

Community Blog
4 MIN READ

Service Factory in Practice

JackLacava's avatar
JackLacava
Community Manager
4 months ago

The Service Factory is a big paradigm shift for OneStream developers. Let's look at some of the capabilities they enable, and how they can help you organize your code in a better way.

The Service Factory model was first introduced in OneStream 8.0, as the cornerstone of what we now call "Dynamic Dashboards". An explanation of its role and how to configure the system to use a Factory is available in the official documentation, but it is often difficult to grasp the difference it makes in actual practice. Here we will cover a few configurations that might help clarify the possibilities we have in our shiny new world of Services and Factories.

Intelligent Dispatching

One might be already familiar with the basic implementation of a Factory: checking the type of call, and dispatching it to a Service of suitable type. For example, the following code will check if a Component Service was requested (because the user clicked on a button with an Action), and dispatch our implementation of such Service.

Select Case wsasType 
   Case Is = WsAssemblyServiceType.Component 
      Return New MyComponentService()

If we look at the signature of the CreateWsAssemblyServiceInstance function that encludes this Select block, however, we might notice that there is a lot more information on the context that we can play with:

This means we can dispatch calls to different services depending on some condition, like the contents of a specific Text property. For example, assuming one implemented a Service that caches the results of some slow SQL query, the following example shows how such service could be enabled or disabled by modifying the Text4 property of the Workspace from which the call originates:

Select Case wsasType 
   Case Is = WsAssemblyServiceType.DataSet
      if workspace.Text4.XFEqualsIgnoreCase("CacheRecords") Then
          return new DataSetCachingService()
     else
          return new DataSetDirectService()
     end if

In a similar manner, we could perform a check on the day of the week (in an extra function added to the Factory class), and perform different Data Management tasks:

Select Case wsasType
      Case Is = WsAssemblyServiceType.DataManagementStep
           If Me.IsThisTheWeekend() Then
                Return new LongRunningBackupJobService()
           Else
                Return new QuickBasicBackupJobService()
           End if

Of course, we could have implemented this check further downstream, inside the actual Service code. However, having very long-winded and complicated classes doing everything in one place, makes solutions hard to read and maintain! Thanks to Assemblies, we can now break up our code into smaller chunks, which are easier to reason about (particularly when coupled with XFProject - more on this in a later post).

Even if you don't like placing this type of check in a Factory, you can "recompose" smaller services with techniques like the ones we will discuss in the next section.

Complex Adapters

In some situations, to favour code reuse and composition, a Service can simply use another Service. 

For example, let's say we want to implement a caching mechanism for some data that is slow to retrieve. We have already implemented a Service that retrieves the data, it works fine and we don't want to touch it; we just want to save its output in some circumstances. So we create a new Service with some custom functions to manipulate the cache, and just invoke the old service when we need to actually fetch data:

Public Function GetDataSet(...) [....]
	Try
		if me.IsCached(args.DataSetName) then
			return me.GetFromCache(args.DataSetName)
		else
			Dim fetchSvc As New DataSetFetchingService()
			Dim ds as DataSet = fetchSvc.GetDataSet(si, globals, workspace, args)
			me.SetInCache(args.DataSetName, ds)
		end if

Note how the code is fairly easy to read, expressing high-level tasks which are then implemented in separate chunks. 

This is, in a way, conceptually similar to the old strategy of calling Business Rules from other Business Rules; but it is somewhat less formal, and of course contained in a single Workspace or Assembly, making it easier to move across applications. 

Advanced Orchestration

Factories must return Service objects, of course; however, there is no rule about having to create such instances right there and then. What if I had a Factory that gets its Services from... another Factory?

For example, let's say we want to build a particular interface in substantially different ways depending on whether the user is an administrator or not. That setting will determine so many things, that you might end up having completely different services for dashboards, components, even Custom Calculate jobs. 

AdminFactory will dispatch to Admin services, and UserFactory will dispatch User services, keeping their logic very clean. And then in EntryFactory, you would just delegate the choice to one factory or the other depending on privilege checking:

Public Function CreateWsAssemblyServiceInstance(...) [...]
	Try
		If BRApi.Security.Authorization.IsUserInAdminGroup(si) then
			Dim myAdminFactory As New AdminFactory()
			Return myAdminFactory.CreateWsAssemblyServiceInstance(si, globals, workspace, wsastype, itemname)
		else
			Dim myUserFactory As New UserFactory()
			Return myUserFactory.CreateWsAssemblyServiceInstance(si, globals, workspace, wsastype, itemname)
		end if
	Catch ex As Exception
	  Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
	End Try
End Function

Instead of having code that checks the privilege for each individual service, polluting our class with repetitive (and error prone) copypasted lines, we have one clear check at the very start of the process, and everything flows from there.

Refactor your Factory!

I hope these brief examples demonstrate, in a practical way, some of the advantages of our Service Factory paradigm. Although initially conceived for tasks related to Dashboards, the Service Factory has now become a key element for any advanced solution built on OneStream, powering everything from XFBR Strings to Dynamic Cubes! So mastering factories will be a must for developers going forward.

Did you ever build a complex Factory you're proud of? Let us know in the comments!

Updated 5 months ago
Version 1.0
No CommentsBe the first to comment