Apex Calls Between Independent Packages

You have a great idea for a process “Orchestrator” utility that can coordinate processes across your existing Force.com products, and perhaps involve some third-party apps too. Orchestrator may want to create, read, update and delete records within these products, but because of its process-oriented nature it will need to initiate existing product processes that have been exposed as Apex APIs.

Your customers may want to use Orchestrator with a variety of product combinations – so they won’t all have the same packages installed. But there’s a problem. Making a call in Apex from one package (Orchestrator) to APIs (Apex methods) in your product packages will create a dependency between the packages:

2013-02-05-DependentPackages

If Orchestrator has the ability to work with any of your products then all of your products must be installed in order to install Orchestrator.

This is not ideal. Orchestrator needs to call the Apex APIs within the other packages, but it also has to be independent of the other packages.

Be dynamic

With dynamic SOQL and dynamic DML we can easily access and manipulate data in other packages without introducing a package dependency.

With Dynamic Visualforce Components we could if we so wished build UIs to display data from several products, again without introducing any package dependencies.

However, as soon as we make a reference in Apex to a class or method in another package, we have implicitly created a dependency upon that package.

Dynamic class instantiation in Apex

Using the forName and newInstance Type methods, it is possible to instantiate a class dynamically. Importantly, use of these methods implies a run-time dependency on the named class, and not a build-time dependency.

The distinction is important for us as package developers – as a build-time dependency in a managed package means either that the class we are dependent on has to be included within our package, or it must already exist in another managed package on which our package depends.

Having dynamically instantiated a class by name, we need to be able to call its methods. However, newInstance returns a generic Object, and there is (currently) no mechanism to call a method by name on an object.

We therefore have to cast the object to a known type before we can call its methods. For example:

Type t = Type.forName('foo','ProcessAPI');
Object o = t.newInstance();
foo.ProcessAPI fooApi = (foo.ProcessAPI) o;
Id result = fooApi.process(myIdList);

Of course by casting the object to its class as we have done here we have caused the very build-time (package) dependency we were seeking to avoid.

“Proxy Plugin Package”

The forName and newInstances become really useful to us when used with Apex interfaces – as an  Apex Plugin.

With an Apex Plugin for a managed package, the package provides a global interface defining what it expects a Plugin class to implement. It uses the Type forName / newInstance methods to instantiate the implementation (the Plugin), which is external to the package, and because the Plugin implements our interface, we can cast the object we receive from newInstance to the interface, and then call any of the interface methods.

Using an interface in this way inverts the normal build-time dependencies. The Plugin has a build-time dependency upon the package, and the package has a looser run-time dependency on the Plugin.

So, if we create a Plugin for our Orchestrator package, which we would deliver in a separate managed package, we can design this Plugin to act as a proxy for a specific product Apex API. It’s job would be to delegate method invocations to the product API methods.

In our “Foo” product package we might have an API:

global class ProcessApi
{
   global Id process(List<Id> idList)
   {
      // do the real work
   }
}

In our Orchestrator package we define an interface for specifying how we intend to call the Foo API:

global interface IFooProcessApi
{
   Id process(List<Id> idList);
}

In our Proxy Plugin Package we have our API proxy, which implements the Orchestrator interface and statically references the product API:

global class FooProcessApi implements orchestrator.IFooProcessApi
{
   foo.ProcessApi m_api = new foo.ProcessApi();

   global Id process(List<Id> idList)
   {
      return m_api.process(idList);
   }
}

In this case we have simply delegated the method invocation, but depending on the design of the API we are accessing, more complex mapping may be necessary within the proxy.

The Proxy Plugin is invoked from within our Orchestrator package, casting the new instance to the interface:

Type t = Type.forName('orchestratorFooPlugin','FooProcessAPI');
Object o = t.newInstance();
IFooProcessAPI fooApi = (IFooProcessAPI) o;
Id result = fooApi.process(myIdList);

Because the Plugin package has a class which implements the Orchestrator’s interface, it has a build-time dependency on Orchestrator; and, because the Plugin package has a static reference to the product API class, it has a build-time dependency on the product package class too.

Our Plugin package therefore has package dependencies on both the Orchestrator package and the product package. It is an extension package for both of these primary packages, forming a bridge: between Orchestrator and the product:

2013-02-05-ProxyPluginPackages

To maintain independence, each product has its own Proxy Plugin Package. This way, it is easy to add or remove individual products from the org.

Using this pattern, we can develop Plugin Proxies for  third-party apps as well as for our own apps.

Furthermore, depending on the design (and purpose) of Orchestrator, we might want to create more generic interfaces within Orchestrator, so that third-parties could develop their own Plugins.

In time we may reflect on this…

In the future we might expect to see Salesforce add support for full reflection in Apex – so that we could invoke methods by name. This would allow us dispense with these Proxy Plugin Packages.

However by adopting the Proxy Plugin pattern I would suggest that switching to use reflection at a later date would be a straightforward transition.

Links

Dreamforce ’12 Developer Session: Making your managed package extensible with Apex Plugins – Stephen Willcock  / FinancialForce.com

Martin Fowler, Patterns of Enterprise Application Architecture – Plugin

Martin Fowler, Patterns of Enterprise Application Architecture – Separated Interface

Force 201 anticipates forName / newInstance methods – there is also a great discussion in the comments to this post

Apex Developer’s Guide – Type Methods

Invoking methods through reflection in Java

Salesforce StackExchange question: Managed Package Integration without Extensions or Dependencies


See also: Nov 2015 Force201 : Breaking managed package dependencies

This entry was posted in Patterns and tagged , , , , . Bookmark the permalink.

One Response to Apex Calls Between Independent Packages

  1. Pingback: Breaking managed package dependencies | Force 201

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s