0

We are trying to develop our code using fflib. Can we access relationship fields directly in the child domain class or do we need to use the parent object domain to access the field values and sent it over as a parameter to the child domain methods?

Approach 1:

main.cls:

List<Contact> contactResult = ContactsSelector.newInstance().selectByEmails(emails);
IContacts contacts = Contacts.newInstance(contactResult); 
Map<String, Set<Id>> contactEmailAndAccountCountryToContactIds = contacts.getEmailAndAccountCountryToIds();

Contacts.cls

getEmailAndAccountCountryToIds() {
  for (Contact contact : getRecords()) {
     String key = contact.Email + '-'+ contact.Account.BillingCountry;
     //...
  }
}

Approach 2:

main.cls:

List<Contact> contactResult = ContactsSelector.newInstance().selectByEmails(emails);
IContacts contacts = Contacts.newInstance(contactResult); 
IAccounts accounts = Accounts.newInstance(contacts.getAccounts());

Map<String, Set<Id>> contactEmailAndAccountCountryToContactIds = contacts.getEmailAndAccountCountryToIds(accounts.getCountryById());

Contacts.cls

getAccounts() {
  List<Account> accounts = new List<Account>();
  for (Contact contact : getRecords()) {
    accounts.add(contact.Account) 
  }
  return accounts;
}

getEmailAndAccountCountryToIds(Map<Id,String> countryByAccountId) {
  for (Contact contact : getRecords()) {
     String key = contact.Email + '-'+ countryByAccountId.get(contact.AccountId);
     //...
  }
}

2 Answers 2

1

Yes, you can. First of all it is more about how your team prefer to handle such scenarios, but not about strict rules that everyone should follow. When I am choosing beetween different approaches I think about: "How to make it easy to use in other services?". In this case your Approach 1 is the easiest one, since it requires minimum possible actions:

  1. Select contacts.
  2. Initiate Domain Contacts.
  3. Call required Contacts Domain method.

While Aproach 2 requires more actions from Service to do:

  1. Select contacts.
  2. Initiate Domain Contacts.
  3. Get Accounts.
  4. Initiate Domain Accounts
  5. Get Country By Id from Accounts
  6. Call required Contacts Domain method.

So you can see that it requires much more effort to use Approach 2.

But after all you still can implement approach that will use Accounts Domain. For this approach you need to know, that you can use Domain Layer inside other Domain Layers. So, you can get this code:

main.cls:

List<Contact> contactResult = ContactsSelector.newInstance().selectByEmails(emails);
IContacts contacts = Contacts.newInstance(contactResult);
Map<String, Set<Id>> contactEmailAndAccountCountryToContactIds = contacts.getEmailAndAccountCountryToIds();

Contacts.cls

getEmailAndAccountCountryToIds(Map<Id,String> countryByAccountId) { 
  IAccounts accounts = Accounts.newInstance(this.getAccounts());
  countryByAccountId = accounts.getCountryById();
  for (Contact contact : getRecords()) { 
    String key = contact.Email + '-'+ countryByAccountId.get(contact.AccountId);
    //... 
  } 
} 

getAccounts() { 
  List<Account> accounts = new List<Account>();
  for (Contact contact : getRecords()) {
    accounts.add(contact.Account) 
  } 
  return accounts; 
} 

It is still easy to use for service layer, but now it is much harder to understand what is going on inside Contacts domain. So, I still prefer Approach 1.

1

Selectors are just a mockable reusable wrapper around SOQL that can be independently unit tested.

A properly named selector like

 ContactsSelector.newInstance().selectWithAccountByEmail(emails)

tells the developer that the resulting SObjects include relationship fields.

I have selectors fetching relationships all over the place just as if I were doing normal inline SOQL --but I have the advantages

  • Something I can now mock with dependency injection (i.e. ApexMocks),
  • unit test,
  • exploit field sets, and
  • have query factory composition so I can avoid having to think about which fields I need in every child or parent SELECT. Sure, I might return more fields than I need but unless there is some particular performance requirement, I prefer the computer to be my servant.

If there is some reason to apply Account domain logic to the Contact.Account relationship SObject, then by all means pass it to the Accounts domain to return something useful to the code that called the Contacts selector. But this is usually not necessary and you can just use theContact.Account.Name etc. without having to resort to delegation to the Accounts domain.

1
  • When we should use accounts domain vs use parent relationship fields directly in child domain? Do you have any guidelines which you follow? Sometimes it is difficult to decide which approach should be used and the whole scope of the domain layer if things are mixed.
    – Praveen
    Commented Sep 5 at 11:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .