Read more at jamessimone.net, or return to the homepage





Dependency Injection & The Factory Pattern

Welcome back to another episode in the continuing Joys Of Apex discussion. In my last post, I covered how to wrap your SFDC DML operations using Crud / CrudMock. As we head into the New Year, let's talk about Dependency Injection (DI) - popularly used to simplify object creation, allow for polymorphic construction of objects, and easily stub out object dependencies while writing tests.

linkIntro to Dependency Injection in Apex

But let's take it slow. What does Dependency Injection look like? Let's take some simple Apex examples:

classes/AccountWrapper.cls
1linkpublic class AccountWrapper {

2link private final Account account;

3link

4link public AccountWrapper() {

5link this.account = [SELECT Id FROM Account LIMIT 1];

6link }

7link}

This is literally the simplest wrapper object possible. But if you ever want to change the reference to the Account being stored in the constructor, or mock it for testing the AccountWrapper's functionality, you're going to need to go with another approach — injecting the Account SObject via the constructor:

classes/AccountWrapper.cls
1linkpublic class AccountWrapper {

2link private final Account account;

3link

4link public AccountWrapper(Account account) {

5link this.account = account;

6link }

7link}

Now, it becomes the object responsible for initializing the AccountWrapper to pass in the correct Account reference; this also means that you can easily create a dummy account for your tests when you don't require a specific account to test your wrapper's functionality.

linkThe Problems with Dependency Injection

Dependency injection thus becomes your one-stop shop, especially when making sensible unit tests, for passing in only the objects your class needs in order to test what you're testing. Dependencies that aren't used on the code path you're testing can be omitted, or mocked through a shared interface or extended mock object. But it wouldn't be Apex without a few roadblocks thrown your way care of Salesforce — because Apex is compiled by pushing your local classes up to the cloud, you can run into issues where refactoring your existing objects can be made difficult when changing the signature of an object's constructor. Anybody who's ever waded through the dreaded Dependent Class Is Out Of Alignment error when deploying your Apex classes knows that if your objects are all being constructed via the new keyword in many different places, correctly refactoring your existing initialization statements to properly match your constructor signature can be ... difficult, or at the very least, time-consuming. Time is precious.

But how can we fix this problem? If we need to dynamically inject classes with other classes at runtime yet still have the flexibility to swap out those dependencies when testing, is there any way to do so?

Important preamble — I have found great success in using the method I am about to describe. If it doesn't float your Object-Oriented Boat, please accept my apologies.

linkUsing the Factory Pattern In Apex

The Factory Pattern offers a centralized place where your objects are constructed. Typically, the Factory ends up looking something like this:

classes/Factory.cls
1linkpublic abstract class Factory {

2link public static AccountWrapper getAccountWrapper() {

3link return new AccountWrapper(

4link //ruh roh, we still have this hard-coded dependency somewhere

5link //more on that later

6link [SELECT Id FROM Account LIMIT 1]

7link );

8link }

9link}

And whenever you need to initialize the AccountWrapper:

1link//some code, somewhere, that needs the wrapper

2linkAccountWrapper wrapper = Factory.getAccountWrapper();

What are the benefits of this approach? Well, now our "client" code (so-called because it is the consumer of initialized AccountWrappers) doesn't need to know how the object is constructed; it's agnostic of the wrapper's dependencies, which can change without having to refactor all of the places using the object. But things still aren't ideal. Initializing the objects within the Factory has brought about the centralization of how your objects are being constructed ... but the Factory itself now runs the risk of becoming extremely bloated. We'd like for the Factory to be responsible for object initialization without having to know exactly what each object needs. That way, it expands in size only so much as you need to create objects, instead of exponentially as your object's dependencies change and increase. Luckily ... there's a way.

linkThe Factory Pattern & Constructor Injection

Let's take our Factory example in Apex and improve it, slightly:

1linkpublic virtual class Factory {

2link public virtual Account getAccount() {

3link return [SELECT Id FROM Account LIMIT 1];

4link }

5link

6link public virtual AccountWrapper getAccountWrapper() {

7link return new AccountWrapper(this);

8link }

9link}

10link

11linkpublic class AccountWrapper {

12link private final Account account;

13link

14link public AccountWrapper(Factory factory) {

15link this.account = factory.getAccount();

16link }

17link}

Wowza. The Factory stays slim, expanding only as you add new objects to your codebase; the responsibility for construction still lies with the object, and it always has access to the dependencies it needs! That's pretty cool. You can also stub out the getAccount method, should you need to fake your account dependency in tests. Even nicer, if the getAccount method is one used frequently in tests, you can also centralize your overrides in a shared test file.

But let's bring it back to our Crud example:

1linkpublic virtual class Factory {

2link public ICrud Crud { get; private set; }

3link

4link private static Factory factory;

5link

6link @testVisible

7link protected Factory() {

8link this.Crud = new Crud();

9link }

10link

11link public static Factory getFactory() {

12link //production code can only initialize the factory through this method

13link if(factory == null) {

14link factory = new Factory();

15link }

16link

17link return factory;

18link }

19link

20link public static BusinessLogicThing getBusinessLogicThing() {

21link return new BusinessLogicThing(this);

22link }

23link

24link @testVisible

25link private Factory withMocks {

26link get {

27link this.Crud = new CrudMock();

28link return this;

29link }

30link }

31link}

32link

33link//your production code

34linkpublic class BusinessLogicThing {

35link private final ICrud crud;

36link

37link public BusinessLogicThing(Factory factory) {

38link this.crud = factory.Crud;

39link }

40link

41link public void handleBusinessLogic(List<Account> accountsNeedingBusinessLogicDone) {

42link this.updateAccountValues(accountsNeedingBusinessLogicDone);

43link this.crud.doUpdate(accountsNeedingBusinessLogicDone);

44link }

45link

46link private void updateAccountValues(List<Account> accounts) {

47link for(Account acc : accounts) {

48link //do business stuff here

49link //for now let's set some silly value

50link acc.Name = acc.Name + ' New';

51link }

52link }

53link}

54link

55link//your test code

56link@isTest

57linkprivate class BusinessLogicThing_Tests {

58link @isTest

59link static void It_Should_Update_Accounts_Correctly() {

60link //Given

61link String testString = 'Test';

62link Account fakeAccount = new Account(Name = testString);

63link

64link //When

65link BusinessLogicThing bizLogic = Factory.getFactory().withMocks.getBusinessLogicThing();

66link bizLogic.handleBusinessLogic(new List<Account>{ fakeAccount });

67link

68link //Then

69link Account updatedAccount = (Account) CrudMock.Updated.Accounts.singleOrDefault;

70link System.assertEquals(testString + ' New', updatedAccount.Name);

71link }

72link}

linkSumming up Dependency Injection & The Factory Pattern In Apex

So what happened here? The Factory in Apex got initialized with the production level Crud wrapper - and in the production level code, we know that the Crud's doUpdate method is going to correctly update Accounts run through the BusinessLogicThing. But in the tests, we can completely avoid having to hit the database and the subsequent requerying to verify that the Account Names have been updated accordingly. We can simply go to our mock and verify that:

That's the power of the Factory Pattern when used with Dependency Injection. Typically, test slowness is the result of the Salesforce database being hit. So far in the Joy Of Apex, we've covered one possible approach to hitting the database — through DML operations like record creation, upserting, updating, deleting, or undeleting.

There is another side to test slowness, though — accessing the database through querying. In an upcoming episode in this ongoing series, I'll cover how you can use the Repository Pattern similarly to our Crud implementation to hot-swap out querying the database while in tests. And once we've covered the Repository ... that's it. There have been some voicing the opinion that this code is a lot of boilerplate; on the contrary, I find that organizations making use of these files typically save on lines of code, test complexity, and developer overhead. That last point is particularly important, and I'd like to give a short explanation:

Without the use of fancy tooling (and I am aware that there are some tools out there to do this, but so far the Salesforce sponsored tooling for VSCode is still kind of disappointing for non-DX orgs), onboarding new developers or consultants to an existing Salesforce.com project often involves hours and hours of head's-down code-digging, particularly to understand how objects are constructed. In an ideal world, where we would have access to niceties like Visual Studio's running count of how many times and where objects are being used, this would be less of an issue; but, considering that most developers are either clinging close to MavensMate/Sublime or VSCode and some combination of VSCode plugins, giving developers a one-stop-shop for where objects are initialized helps to familiarize them with the codebase and object interplay much faster.

Here's to hoping you enjoyed this episode of The Joys Of Apex. More to come in 2020 — for now, wishing you all a Happy New Year!

PS — I did some stress testing on the use of the CrudMock / Crud class I am recommending versus the FFLib Apex Mocks library which was developed by Andrew Fawcett, who worked on FinancialForce prior to working for Salesforce. FinancialForce's approach closely aligns with that of Mockito, one of the pre-eminent Java mocking solutions, and as such is widely accepted in the industry as the de-facto way to approach mocking within Apex. I will also be covering this in a future post, but for now if you are curious, check out the project on my Github for a sneak peek of the relative performance merits for each library. Cheers!

The original version of Dependency Injection & The Factory Pattern can be read on my blog.

Intro to Dependency Injection in ApexThe Problems with Dependency InjectionUsing the Factory Pattern In ApexThe Factory Pattern & Constructor InjectionSumming up Dependency Injection & The Factory Pattern In Apex

Home Apex Logging Service Apex Object-Oriented Basics Batchable And Queueable Apex Building A Better Singleton Continuous Integration With SFDX Dependency Injection & Factory Pattern Enum Apex Class Gotchas Extendable Apis Future Methods, Callouts & Callbacks Idiomatic Salesforce Apex Introduction & Testing Philosophy Lazy Iterators Lightweight Trigger Handler LWC Composable Modal LWC Composable Pagination LWC Custom Lead Path Mocking DML React Versus Lightning Web Components Refactoring Tips & Tricks Repository Pattern setTimeout & Implementing Delays Sorting And Performance In Apex Test Driven Development Example Testing Custom Permissions Writing Performant Apex Tests



Read more tech articles