A few weeks ago, Andy Fawcett introduced a new open source project via his blog post, Managing Dependency Injection within Salesforce. If you haven’t read it yet, I encourage you to do that first.

Since then, I’ve been collaborating with Andy Fawcett and John Daniel on developing new features and use cases for the project. The Salesforce platform provides amazing power through its declarative “clicks-not-code” features, and there’s no reason why those capabilities shouldn’t be possible with advanced design patterns like dependency injection and packaging in this new age of Salesforce DX.

So I’m happy to announce dependency injection support for executing flows in Apex, Visualforce, and Lightning Components where the flow can be defined in one package and its usage determined dynamically at runtime in another package.

New to unlocked packages? Check out the Salesforce Developer’s blog series on the topic, as well as check out the Trailhead module Unlocked Packages for Customers.

Use Case

Say you’ve got a flow that accepts some input variables, like the below screen shot, and you want to surface this flow in different places in your org based on various criteria.

account_record_flow_variables.png

And let’s consider that based on business requirements, the specific flow that gets surfaced to your end-users may also vary and that your team has decided to build and manage the different flows in their own unlocked packages because these flows are not related, depend on separate schema and data, and otherwise have nothing to do with each other and it makes sense to manage them in their own discrete packages aligned with their specific dependencies.

To avoid tight-coupling of the packages defining each of these various flows and the packages of where the flows will be surfaced (e.g. sales app package, marketing app package, or a package that gets deployed to multiple orgs and the flow to surface changes by org.), we can leverage dependency injection pattern supported by the force-di open source project.

In the rest of this blog post, I’ll explore surfacing flows via dependency injection in lightning componentsvisualforce pages, and apex.

Injecting Flows in Lightning Components

For this example, I’ve deployed force-di into my org and defined a custom metadata binding record specifying the flow to execute when the binding is evaluated at runtime.

account_record_flow_cmdt

My code can reference flow_AccountRecord custom metadata record and at runtime the framework will resolve the binding to surface the flow whose developer name equals AccountRecordFlow. An admin can change this binding value at any time to surface a different flow. To support this just-in-time design, force-di dynamically creates a lightning:flow component and adds it to the page.

Here’s a screen shot of what my example component looks like using force-di to inject a flow and pass input variables to it:

account_record_flow_ltng_cmp.png

  • On line 17, I use the injector tag and specify the binding to use.
  • On lines 18-23, I specify the flow input variables to pass in.
  • On line 10, I specify an event listener to receive the flow’s output variables, if any.

The power of using the injector component and binding name is that the actual flow’s metadata does not have to be defined in the same package or org where this component that uses the flow resides at compile time; they are de-coupled. You might want to re-read that sentence and let that sink in.

This de-coupling is necessary if your team wants to move towards adopting unlocked packages to build and release discrete logical apps.

This de-coupling simplifies my development in this scratch org because it means that I can provide a simple mock flow rather than needing all of the other dependencies any possible flow that might surface here may need, such as unrelated schema or data. All I need to make this component functional in my scratch org is a flow that accepts the prescribed input variables.

Putting it all together, in this example I added the component to my account record page. So now when users view accounts, force-di resolves the binding and loads the configured flow.

account_record_flow_page

Admittedly, my flow doesn’t do much, it just prints the input variables to a screen. But what the flow is doing is not the point, the point is that I can change what flow appears in the red box at runtime by changing the binding metadata record. Or, if I’m using apex configuration to define my bindings rather that custom metadata, then I could have programmatic logic perform SOQL queries or any other business logic to determine which flow should appear in that red box. Amazing! So for quick development of the component and record page in say Package A can be easier because I don’t need all the dependencies that the real flow that goes in the red box may need. And with dependency injection, my component really doesn’t care what flow goes there, just that the flow that does go there adheres to some contract on expected input and output variables.

Excessive or Necessary

You may be thinking, “Doug, this is a bit excessive when App Builder already supports dynamic components. Couldn’t I just add a handful of flows to the page and use conditional logic to show/hide them?”

You absolutely could use dynamic components, but that would mean if you’re adopting packages then the package with this lightning page would also need to include the metadata for all possible flows you want to configure in it, too. And that is the opposite of our goal with modularizing our org’s metadata (de-coupling) and adopting unlocked packages (separation of concerns).

Let’s now look at dependency injecting flows in Visualforce and Apex contexts.

Injecting Flows in Visualforce

Like Lightning Components, we can also dynamically create Visualforce components. At runtime, force-di creates a flow:interview Visualforce component and adds that to the page.

account_record_flow_vfpage

You may be thinking, “cool, I just pass in a map if input variables and the injector tag will handle passing those into my flow at runtime”. That was the dream… but sadly, it is not reality. Or at least, it’s not as easy as all that, there is a bit more effort on your end to make this work.

No Dynamic Parameters

To pass input variables to the flow gets a bit complicated due to limitations with dynamic Visualforce components.

Normally, to set input variables on a flow in Visualforce, then you nest apex:param tags inside the flow:interview tag, for example:

account_record_flow_vfinterview.png

Unfortunately, the apex:param tag cannot be dynamically created in Apex at runtime to be injected into the dynamically created flow:interview tag. Nor do the name attributes on flow:interview and apex:param tags support dynamic references like {!dynamicName}. The name attribute must be a literal value. This meant we could not do something like this:

account_record_flow_vfrepeat.png

So with those two limitations, we had to devise a workaround to support dynamically injecting flows in Visualforce pages.

Please consider voting for the idea to add Map variable type for flows.

Workaround: Thread Locals

If you’ve ever done multi-threading in other programming languages like Java or .NET, then you may have heard the term thread local storage. It is a way to define global variables (e.g. static variables) whose values are scoped to a single thread. Another way to describe it is they are request scoped variables to a specific user’s request of your application. That way multiple threads (e.g. multiple users accessing the same web page at the same time) can concurrently define and use the same global variable names but without interferring with the other threads (i.e. the other users’ requests). In contrast to session scoped variables, request scoped variables are temporary and only last for the duration of that request (e.g. the time to process a single web page request. Clicking a link or refreshing the page would be a new request with its own request scoped variable values).

So how does this help us?

Well, any time you access a Visualforce page, you are making a web request for that page. The Salesforce platform spins up a new thread and Apex transaction to handle your request, do what it needs to do (e.g. query data, execute apex, perform DML, etc.), then finally displays the generated page. A special behavior of static variables in Apex, as compared to Java, is that a static variable in Apex is only static within the scope of the request/transaction and its value is reset across transaction boundaries.

Again, so how does this help us?

Static variables in Apex are by definition request scoped variables, that is, thread locals.

Armed with this information, force-di, when dynamically injecting a flow into a Visualforce page, sets all the injected input variables as static Apex variables on an invocable Apex class named BindingParam. Your flow can then retrieve the values of these static variables by invoking the BindingParam Apex action for each variable you want to retrieve from the injected parameters.

Here’s a screen shot of the BindingParam apex action in the Flow Designer’s palette:

flow_bindingparam_palette.png

You configure one input on the Apex action, the name of the request parameter whose value to retrieve:

flow_bindingparam_inputs.png

You configure one output on the Apex action, the data type of the retrieved parameter and which flow variable to assign the value to.

flow_bindingparam_outputs.png

By far, this is not ideal. But given the limitations at hand with dynamic Visualforce, I’m not aware of a good alternative (other than switching to Lightning Experience and using Lightning Components, ha!).

With all that said, to demo dependency injection of a flow in Visualforce, I wanted to keep the logic and concerns of my original flow separate from the use of BindingParam Apex action. I created a second flow that uses BindingParam Apex action to retrieve my desired parameters, assigns those to flow variables, and then calls the original flow.

account_record_flow_vfsubflow.png

Injecting Flows in Apex

Dependency injection of flows in Apex is, by contrast to Visualforce, far simpler. Borrowing from Andy’s Flow Extensions open source project, we can easily look up a flow instance at runtime, optionally passing in input variables, and retrieving output variables.

account_record_flow_apex.png

Even without the force-di framework, you can dynamically execute an autolaunched flow with Flow.Interview.createInterview( flowName, inputVariablesMap ) method. Just change up the flow name argument to change which flow is executed.

However, the benefit of using the force-di framework is that it already has the tooling to support being custom metadata driven and programmatic driven. Another benefit of using the force-di framework for dependency injecting flows in Apex is because it aids in test driven development, as Andy points out in his blog post Test Driven Development, Mocking, and Force DI.