One Batch Apex to rule them all (Part I)

Don’t repeat yourself

Advertisements

If you have been working with apex some time, you’ll probably know what a Batch Apex is. Even, you probably created more than one batch apex class to support your most demanding processes, right?

But haven’t you being copying and pasting most of the code, to finally amend some bits of it? Don’t they look too similar at the end? Don’t look around, I won’t tell anybody you did it 🙂 It might not be as bad as it seems, because you probably wanted to provide the same user experience and error handling on all your processes. We’ll work on this now. Are you ready?

Identifying the problem

Let’s start looking at two very simple batch routine examples.

The first one looks for accounts that don’t have contacts and calls a service class to create a default one:

public class CreateContactsBatch implements Database.Batchable<sObject>
{
    public CreateContactsBatch() {}

    public Database.QueryLocator start(Database.BatchableContext BC)
    {
        return Database.getQueryLocator('SELECT Id, Name FROM Account WHERE Id NOT IN (SELECT AccountId FROM Contact)');
    }

    public void execute(Database.BatchableContext BC, List<sObject> scope)
    {
        ContactsService.createContacts(scope);
    }

    public void finish(Database.BatchableContext BC) {
        // Send a summary
    }
}

The second one, looks for contacts which account is not active. The Active__c field is a ad-hoc custom field:

public class ActivateAccountsBatch implements Database.Batchable<sObject>
{
    public ActivateAccountsBatch() {}

    public Database.QueryLocator start(Database.BatchableContext BC)
    {
        return Database.getQueryLocator('SELECT AccountId, FirstName FROM Contact WHERE Account.Active__c = false');
    }

    public void execute(Database.BatchableContext BC, List<sObject> scope)
    {
        AccountsService.activateAccounts(scope);
    }

    public void finish(Database.BatchableContext BC) {
        // Send a summary
    }
}

Both classes are doing pretty much the same. The things that vary are just the SOQL performed and the method invoked within the execute method. We could spot some other common aspects, but we’ll keep it simply so far.

Ideally it would be great if we could have a unique batch apex class to manage both processes and their behavior. That’s what is this article about. We are going create a single batch class that runs any kind of operation. We are going to apply some good OOP principles, so be prepared!

Applying the command pattern

Our first task is to separate the things that change and leave the common important logic in the batch class. To accomplish this, we’re going to use the command pattern, which aim is to encapsulate method requests.

Our refactoring strategy will be driven by 3 simple steps:

  1. Create an abstraction that supplies both operations: SOQL construction and the method operation (the command).
  2. Create a Batch class that interacts with the interface.
  3. Implement the interface on each Service class that contains the business logic.

First, as we said, we’ll create an abstraction. This will be the Batchable interface as follows:

public interface Batchable
{
    String getQuery();
    void execute();
}

Then, we have just to create a Batch class that delegates the operations on it:

public class BatchableHandler implements Database.Batchable<sObject>
{
    private Batchable command;

    public static Id runCommand(Batchable command)
    {
        if (command == null)
            return null; // Or say something relevant

        BatchableHandler batchableHandler = new BatchableHandler(command);
        return Database.executeBatch(batchableHandler);
    }

    public BatchableHandler(Batchable command)
    {
        this.command = command;
    }

    public Database.QueryLocator start(Database.BatchableContext BC)
    {
        return Database.getQueryLocator(command.getQuery());
    }

    public void execute(Database.BatchableContext BC, List<sObject> scope)
    {
        // No error handling by now
        command.execute(scope);
    }

    public void finish(Database.BatchableContext BC) {
        // We'll add something here to enhance our batch soon
    }
}

Easy, isn’t it? This batch can now deal with any kind of operation which implements the Batchable interface. It ignores what operation is being executed and the data being processed. It is now decoupled from the operations performed! We have additionally simplified the invocation by creating the static runCommand method.

Third (and finally) we have to make the services to implement the interface. Let’s see how to do it for the ContactsService class. You can try doing it yourself for the ActivateAccountsService one.

public with sharing class ContactsService
{
    public class CreateContactsBatchable implements Batchable
    {
        public String getQuery()
        {
            return 'SELECT Id, Name FROM Account WHERE Id NOT IN (SELECT AccountId FROM Contact)';
        }

        public void execute(List<Account> accounts)
        {
            createContacts(accounts);
        }
    }

    public static void createContacts(List<Account> scope)
    {
        // This is the existing logic we already had in the class.
        // Do something here, like create contacts from the given accounts list
    }
}

That’s all! Run it, play with it, make it fail and improve it. I look forward to get feedback from you.

In later posts we’ll enhance this a bit more. For instance, we’ll see how to make the batch class to run several operations using batch chaining. Also, we’ll make the class a bit more handy by adding error handling and notifications.

Stay tuned. See you soon!

Conclussions

Applying this pattern we have immediate benefits from it:

  1. Each time we need a new background process, we have just to make the container class to implement the Batchable interface. We don’t need to change the batch class itself. In other words, the batch class is open for extension and closed for modification.
  2. Inherited from the point 1, we get a robust framework to make all our processes to be batch capable. It can be tested properly and we won’t need to worry about it again.
  3. The batch class can submit any kind of operation without knowing about the underlaying code. We have programmed an interface, not an implementation.

Why’s

  • Why did we use the command pattern? Couldn’t we just modify the batch class to be abstract and extend from it whenever we need it? The answer is “yes, of course”. We could use the Template Method pattern instead. However I decided to use the command pattern because composition is always more flexible than inheritance. In next articles we’ll see how we can benefit from this approach by making our batch class to take a list of commands. We’ll be able then to process a queue of requests, which is actually quality of the command pattern.
  • Why the batch classes are not using the with/without sharing keywords? This is to provide flexibility. The sharing permissions are inherited from the caller, unless they are specified in the current class. We will be a bit more permissive by letting the caller to decide the security level. As you can see, the ContactsService class uses the with sharing keyword.
  • Why is the CreateContactsBatchable an inner class in ContactsService? Could we just make the ContacsService to implement the Batchable interface? Yes, we could. It would asume the class is doing just one thing (create contacts), which is a good practice indeed. However, ContactsService could probably provide more services that interact with contacts in the future. This is up to you.

9 thoughts on “One Batch Apex to rule them all (Part I)”

  1. Nice article, I like the idea. I am wandering how could we make this pattern work for cases where some batches require Stateful interface and some not? I assume, for part two you will enhance this example with start/stop methods so here would be perfect moment to think about how one can preserve state through contructor=>start=>execute=>finish cycle.

    Best regards

    Like

    1. Thanks for your comment. Interesting thoughts. Making the BatchableHandler able to implement the Database.Stateful interface or not, would require some composition mechanism. But I would place such distinction in another layer to make the main behavior independent from that fact.

      By now, the following articles will be focused on completing the functionality of this “framework” to make it fully functional. This will include error handling, of course, so it will stop or continue on demand. I’ll include some thoughts about the statefulness of the batch apex jobs.

      I’ll keep your comments in mind. Hope you find next posts interesting too!
      Cheers

      Like

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 )

Google+ photo

You are commenting using your Google+ 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 )

Connecting to %s