Code Monkey home page Code Monkey logo

Comments (6)

wimvelzeboer avatar wimvelzeboer commented on July 18, 2024 2

Hi @parsam97,

The thought behind splitting the domain from the triggerhandler is to limit the scope of the domain class. The domain is now just a wrapper around a List of SObjects, providing methods to get & set fields, and to filter the SObjects. It shouldn't do something more.
If you would to combine a UnitOfWork with a domain method, that will immediately render that method useless if you want to use the same domain method in a before Insert/update context.
I would leave the UnitOfWork to the abstraction level that is calling the domain methods, e.g. a service method.
Alternatively you could add method overloads in the domain to handle that (see my example at the bottom).

Looking at your example, One thing I notice is that the domain goes beyond its scope, and invokes a selector. In those cases you would want to introduce a service method.
I also see that the trigger runs on Interaction__c in a before insert context, which means that you do not require a unitOfWork. You can just set the fields with a domain setter method.

I would do something like:

InteractionsTriggerHandler.cls

public override void onBeforeInsert()
{
  InteractionsService.assignOwnership(getRecords());
}

InteractionsService.cls

public static void assignOwnership(List<Interaction__c> records) {
   Service.assignOwnership(records);
}

private static IInteractionsService Service() {
    (IInteractionsService) Application.Service.newInstance(IInteractionsService.class);
}

InteractionsServiceImpl.cls

public void assignOwnership(List<Interaction__c> records) {
   assignOwnership(Interactions.newInstance(records));
}
public void assignOwnership(Interactions domain){
    // Collect channels
    IChannels channels = Channels.newInstance(domain.getChannelIds());

   // Create a map of key = Channel__c.Id to Channel.OwnerId
   Map<Id, Id> ownerIdByChannelId = channels.getOwnerIdById();

   domain.setOwnerIdByInteractionChannelId(ownerIdByChannelId);
} 

Interaction.cls

public static IInteractions newInstance(List<Interaction__c> records)
{
  return (IInteractions) Application.Domain.newInstance(records, Schema.Interaction__c.SObjectType);
}

public IInteractions setOwnerIdByInteractionChannelId(Map<Id, Id> ownerIdById) {
  for (Interaction__c record : (List<Interaction__c>) getRecords())
  {
    if (ownerIdById.containsKey(record.Interaction_Channel__c) == false) {
      continue;
    }
    record.ownerId = ownerIdById.get(record.Interaction_Channel__c);
  }
  return this;
}

And you could add a method overload to the domain method for the unitOfWork:

public IInteractions setOwnerIdByInteractionChannelId(fflib_ISObjectUnitOfWork uow, Map<Id, Id> ownerIdById) {
  setOwnerIdByInteractionChannelId(ownerIdById);
  uow.registerDirty(getRecords(), new List<SObjectField>{ Interaction__c.OwnerId });  // only register the modified field as dirty
}

If you would also use the fflib-apex-extensions package and extend your domain from fflib_SObject2, then you could even change the setOwnerIdByInteractionChannelId into this:

public IInteractions setOwnerIdByInteractionChannelId(Map<Id, Id> ownerIdById) {
 setFieldValue(
     Schema.Interaction__c.Interaction_Channel__c,
     Schema.Interaction__c.OwnerId,
     ownerIdById);   
 return this;
}

from fflib-apex-common.

parsam97 avatar parsam97 commented on July 18, 2024

Hi @wimvelzeboer,

Really appreciate you taking the time to reply and help me out 🙏🙏

Since my posting this question, I got Andrew Fawcett's 4 edition enterprise patterns book, where I learned more about CDCL and SDCL approaches. I've since decided to use the CDCL approach to simplify things for myself as I am just starting out.

The code you kindly provided still leaves me with the following question:

What if I there's a requirement to assign ownership (or something similar of the sort) onAfterInsert?
If I understand your suggestions correctly, this would mean the domain class should call the InteractionService to assign ownership onAfterInsert? It seems wrong to me because the InteractionService is just going to call the domain again, which is where we start from. Although yes, the service method would collect the necessary Channel__c data. But are we doing this just so we don't have our domain layer calling the selector layer or is there another reason?

Thanks again.

from fflib-apex-common.

wimvelzeboer avatar wimvelzeboer commented on July 18, 2024

@parsam97
In the scenario of an on after insert, I would do the following:

InteractionsTriggerHandler.cls

public override void onAfterInsert()
{
  fflib_ISObjectUnitOfWork uow = Application.UnitOfWork.newInstance();
  InteractionsService.assignOwnership(uow, getRecords());
  uow.commitWork();
}

InteractionsService.cls

public static void assignOwnership(fflib_ISObjectUnitOfWork uow, List<Interaction__c> records) {
   Service.assignOwnership(uow, records);
}

private static IInteractionsService Service() {
    (IInteractionsService) Application.Service.newInstance(IInteractionsService.class);
}

InteractionsServiceImpl.cls

public void assignOwnership(fflib_ISObjectUnitOfWork uow, List<Interaction__c> records) {
   assignOwnership(uow, Interactions.newInstance(records));
}

public void assignOwnership(fflib_ISObjectUnitOfWork uow, Interactions domain){
    // Collect channels
    IChannels channels = Channels.newInstance(domain.getChannelIds());

   // Create a map of key = Channel__c.Id to Channel.OwnerId
   Map<Id, Id> ownerIdByChannelId = channels.getOwnerIdById();

   domain.setOwnerIdByInteractionChannelId(uow, ownerIdByChannelId);
} 

Interaction.cls

public static IInteractions newInstance(List<Interaction__c> records)
{
  return (IInteractions) Application.Domain.newInstance(records, Schema.Interaction__c.SObjectType);
}

public IInteractions setOwnerIdByInteractionChannelId(Map<Id, Id> ownerIdById) {
  for (Interaction__c record : (List<Interaction__c>) getRecords())
  {
    if (ownerIdById.containsKey(record.Interaction_Channel__c) == false) {
      continue;
    }
    record.ownerId = ownerIdById.get(record.Interaction_Channel__c);
  }
  return this;
}

public IInteractions setOwnerIdByInteractionChannelId(fflib_ISObjectUnitOfWork uow, Map<Id, Id> ownerIdById) {
  setOwnerIdByInteractionChannelId(ownerIdById);
  uow.registerDirty(getRecords(), new List<SObjectField>{ Interaction__c.OwnerId });  // only register the modified field as dirty
}

You would still use the same method to update the field, but you reach that method via slightly different method overloads with the unit-of-work as extra parameter.
So, the domain would indeed not call a selector class.

from fflib-apex-common.

parsam97 avatar parsam97 commented on July 18, 2024

Got it, thanks for the help again!

from fflib-apex-common.

parsam97 avatar parsam97 commented on July 18, 2024

public void assignOwnership(fflib_ISObjectUnitOfWork uow, Interactions domain){

@wimvelzeboer You mentioned this, including the domain as an argument to the Impl class. Did you do this so we could later call service method from domain like this: InteractionService.assignOwnership(this) ?

from fflib-apex-common.

daveespo avatar daveespo commented on July 18, 2024

Closing this as it's not a bug or feature request and has been largely addressed by William (thank you!)

from fflib-apex-common.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.