The Daily Parker

Politics, Weather, Photography, and the Dog

Integrating Multiple CRMs with One Azure Cloud Service

(This is cross-posted with the 10th Magnitude Tech Blog.)

Part 1: The Challenge

We developed efox, a Microsoft Windows Azure PaaS application, for our customer Holden International over the course of the last year. Our biggest challenge in the first release was to integrate their flagship sales training application, efox, with SalesForce. But from the beginning of the SalesForce integration effort, we had a constraint we couldn’t ignore: Not only would efox have to integrate with SalesForce, but it would also have to integrate with Oracle Siebel CRM, Microsoft Dynamics, and whatever other CRM application Holden’s customers want to throw at it.

At first glance it looks like a simple problem. Lots of applications, after all, integrate with CRMs. There are also lots of tools to help: SalesForce has a decent SDK, Microsoft has Windows Identity Foundation, most providers (including SalesForce) support OAuth...been there, coded that.

In a typical integration, the custom application extends the service provider’s application. For example, SalesForce has tools to create custom SalesForce extensions that extend SalesForce functionality. An application can run as a SalesForce user and share data with SalesForce transparently, using OAuth and the SalesForce REST API. This is, in fact, how the previous version of efox worked.

efox inverts the problem. Yes, it’s easy to integrate an application with one CRM; but it’s hard to integrate with all of them—simultaneously. At any moment, efox could have several hundred concurrent users going to half a dozen different CRM systems. The application therefore needs to treat each user’s session and each CRM request as a discrete unit.

In this and two succeeding posts, I’ll outline how we solved the architectural problem of separating the UI and back end to abstract away the specific CRM implementation from everything else; how we use ASP.NET MVC 4 routing to let people click a button in their CRM system to launch efox; and how we added single sign-on (SSO) using SAML to allow each user SSO access to efox—or not.

Part 2: The Architectural Solution

Before trying to implement an solution, we first designed the application to handle any arbitrary CRM integration we cared about.

First we created an interface, ICrmIntegration, that exposes these basic operations:

IEnumerable<Account> Accounts(Guid userId);
IEnumerable<Contact> Contacts(int accountId);
SalesPlan GetSalesPlan(string crmOpportunityId, Guid userId);
CrmLoginResult Login(string userName, string password, out string message);
IEnumerable<Opportunity> Opportunities(int accountId);
void Save(SalesPlan salesPlan);

The application uses Factory classes to retrieve objects from the database. We created new Integrated_Factory classes (like IntegratedSalesPlanFactory) that are aware of CRM integration classes and aware of the concrete Factories. Instead of calling the concrete factories, the UI now calls the Integrated factories, which decide whether to retrieve objects from the database or from the user’s ICrmIntegration.

This design completely abstracts the concrete CRM integrations away from the application’s UI. A controller class needing to retrieve a list of Account objects for the user calls IntegratedAccountFactory.Find(userId). That method calls the IntegrationFactory to get the user’s CRM integration (an ICrmIntegration implementation). If IntegrationFactory returns null, meaning the user isn’t integrated with a CRM system, then the IntegratedAccountFactory calls the regular AccountFactory instead. Otherwise it calls the Accounts method on the integration object to retrieve the list of accounts from the user’s CRM system.

Here’s the code in IntegratedAccountFactory:

public static IEnumerable<Account> Find(Guid userId)
{
	var tenantId = AccountFactory.TenantId(userId);
	if (!tenantId.HasValue) return AccountFactory.All(userId);

	var integration = IntegrationFactory.Instance.Find(tenantId.Value, userId);

	return (null == integration) ?
		AccountFactory.All(userId) :
		integration.Accounts(userId);
}

The UI neither knows nor cares whether the particular user gets her accounts from efox, from SalesForce, or from a squirrel; all it cares about is getting a list of accounts. In a sense, this is a classic bridge pattern.

I'll have a couple more posts on efox's CRM integration over the next few weeks. Next up, I'll talk about how CRM integration looks to the end user, then after that, about how we integrated single sign-on (SSO) with multiple identity providers. Check back in a week or so for Part 3.

Comments are closed