0

Let’s say you have a list of Accounts and you want to populate their birthday with the same date. However, you also want this method to be reusable.

What is the best way to do this in APEX?

So far I’ve been creating a void method that takes in a list of objects and simply updates them as needed but this feels too procedural, i.e when I do this:

setAccountBirthday(accountRecords, birthDate)

I’d rather do something like this, but not sure if it’s over engineered:

AccountsHandler accounts = new AccountsHandler(accountRecords); // custom class to handle account processing
accounts.setBirthday(birthDate);

I am interested in hearing the best practice way of doing this in general and whether I overlooked other options.

3

Methods should generally do only "one thing." A method that updates some field values, then saves to the database, is doing two things. It's okay to have that method, but under the hood, you'd still technically have two methods. Here's what my "setBirthday" method would look like:

public static Data.TransactionResult setBirthdays(Account[] records, Date birthdate) {
    Data.setFieldValues(records, Account.Birthday__c, birthdate);
    return Data.updatePartial(records);
}

Where Data.setFieldValues accepts a list of records, an sObjectField token, and an Object value. This is a very common thing that I do, so having a method like this saves me writing a "for loop" each time, I just plug in the bits I need.

Similarly, Data.updatePartial accepts a list of records, and returns any errors using a custom wrapper.

Alternatively, if I wanted a "smarter" algorithm, I might decide to only update records that were actually changed:

public static Data.TransactionResult setBirthdays(Account[] records, Date birthdate) {
    Account[] working = (Account[])Data.clonesObjectList(records);
    Data.setFieldValues(working, Account.Birthday__c, birthdate);
    Account[] updated = (Account[])Data.diff(records, working);
    return Data.updatePartial(updated);
}

Having a variety of small-ish methods that perform one thing, I can build larger units of work in interesting ways.

2

I'm partial to the Enterprise Architecture patterns promoted by Andrew Fawcett, VP of Product at Salesforce and formerly CTO of Financial Force. You can read about these in his book or discover on Trailhead here and here.

To your specific question, I would use/create a service class AccountsService and then add two methods:

setBirthDates(Set<Id> acctIds, Date birthDate)
setBirthDates(fflib_ISobjectUnitOfWork uow, Set<Id> acctIds, DAte birthDate)

The latter method would be used if the caller wanted the birthdate updates to be part of a larger unit of work (transaction boundary). The former method would establish its own unit of work , call the second method, and then commit the unit of work upon return

public void setBirthDates(Set<Id> acctIds, Date birthDate) {
   fflib_ISObjectUnitOfWork uow = Application.UnitOfWork.newInstance();
   setBirthDates(uow,acctIds,birthDate);
   uow.commitWork();
}

public void setBirthDates(fflib_ISobjectUnitOfWork uow, Set<Id> acctIds, Date birthDate) {
  Account[] accts = AccountsSelector.newInstance().selectById(acctIds);
  for (Account a: accts) {
     a.BirthDate__c = birthDate;
     uow.registerDirty(a);  
  }
}

I'd go one step further and use the Enterprise Pattern to set up for mocking so I could unit test my service class method without having to do any DML. You can read more about Apex Mocking at the GitHub repo (or better still, his latest book 2nd or 3rd edition)

The service layer pattern allows for its execution from many clients - triggers (domain), controllers, REST clients, invocable APEX, etc.

0

It's a bit left-field, but I have an open-source library intended to make things like this simpler. Your example would be written as:

new nebc.LazySObjectIterator(accountRecords)
    .forEach(new nebc.SObjectPutField(Account.Birthday__c, birthDate));

If you wanted to filter out those which already had the desired date, you could do it list this:

List<Account> toUpdate = new nebc.LazySObjectIterator(accounts)
        .filter(new nebc.IsNot(new nebc.IsSObjectFieldEqual(Account.Birthday__c, birthDate)))
        .mapValues(new nebc.SObjectPutField(Account.Birthday__c, birthDate))
        .toList(new List<Account>());

And that right-hand side doesn't just have to be a variable, it can be a function. So, for example, it can be used to copy from one field to another. e.g. to copy the LastName into the FirstName of a list of Contacts, you could do this:

new nebc.LazySObjectIterator(contacts)
        .forEach(new SObjectPutField(Contact.FirstName, new nebc.FieldFromSObject(Contact.LastName)));

It all MIT licenced and available here:

https://bitbucket.org/nebulaconsulting/nebula-core/src/master/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.