apex-enterprise-patterns / fflib-apex-common

Common Apex Library supporting Apex Enterprise Patterns and much more!
BSD 3-Clause "New" or "Revised" License
913 stars 517 forks source link

fflib apex common Usage #210

Closed ab0369 closed 5 years ago

ab0369 commented 5 years ago

We have a existing salesforce implementation, we have most of the logic in triggers and trigger handlers. We are still using classic and no mobile or other UI. But still we are planning to implement apex common library for SOC

1. Is there any other better pattern to refactor domain classes that are huge (1000 lines of code )?

We have huge amount of logic in trigger handlers, once we refactored this in selector, domain, service and uow. We still have large amount of code in domain classes. We wrote some helper classes with public methods. .

eg. we have 30 field validations and 20 field defaults and the field defaults. Related object updates etc.

2. Apply Defaults - OnBeforeUpdate event We have default fields onbeforeupdate event too, for this I extended the domain base class with this method, I know this is not upgrade safe. Is there a better option ? or should I just use onBeforeUpdate method ?

public virtual void onApplyDefaults(Map<Id,SObject> existingRecords) { }

3. Use custom Setting to Turn trigger on and off as needed We have custom setting to to turn on and off triggers. for now it seems we need to write extra line of code in the trigger. is there any other better option for this ?

public trigger testtrigger(allevents){ if(customsetting){ domainclass.getTriggerEvent(YourDomain.class).disableAll();

//or just return; } call domainclass here }

Alex-MIL-ITA commented 5 years ago

I am also very keen to see trigger "toggles" or "kill switches" implemented in FFLIB - they are pretty universal need, and something that shouldn't require custom code to implement.

stohn777 commented 5 years ago

Hi @ab0369.

Here's some musings on my part, and certainly not an end all and be all reply. :innocent:

1. Is there any other better pattern to refactor domain classes that are huge (1000 lines of code )?

It seems highly likely, with over 1000 lines of code, that more than one logical concept is being executed. First, consider decomposing the domain-class logic into smaller chunks of code that are more single purpose (SOLID design principles) and easier to test and maintain. Also with decomposition, patterns may be discovered that allow the elimination of redundant or nearly-so code.

Otherwise FFLIB doesn't provide features for organizing logical code into more manageable chunks, but patterns and practices communities have numerous suggestions. Consider the strategy pattern as a potential pattern, and represents some work that I've seen in Apex development lately.

2. Apply Defaults - OnBeforeUpdate event

FFLIB's default-value functionality is, indeed, executed onBeforeInsert, but nothing prevents an extension, especially if it's a sub-class of fflib_SObjectDomain which would be more resilient to updates.

Disclaimer: I've not tried extending fflib_SObjectDomain lately to know what hurdles may lie hidden therein. But I might give it another try just for edification sake. :nerd_face:

3. Use custom Setting to Turn trigger on and off as needed

A trigger-enablement switch is a worthy concept, yet allowing for logic injection so that developers may provide the determining logic is a bit more interesting. Something using an interface of this nature for injection:

interface fflib_ITriggerStatus {
    Boolean isEnabled();
}

Regards.

afawcett commented 5 years ago

Hello! Sorry for the late response.

RE: 1, helper classes are good, but I would also consider domain base classes per the chapter in my book this can help you group shared logic between certain objects that have similar behaviors. You can use virtual or abstract methods in your own base classes much like the fflib_SObjectDomain to allow extension classes to customize your base classes behavior if need. You may also consider splitting groups of field validations into separate classes that implement a standard interface (e.g. IFieldValidator.validate) that can be used by the domain class to build a list of applicable validations based fields that have changed then execute them. This also helps with performance. Be careful though with SOQL/DML done in such classes, try to pass in the required state if possible to the validators.

RE: 2, yes that does appear to be a missing feature. Meanwhile to make to avoid editing the base try public override void handleBeforeUpdate(Map<Id,SObject> existingRecords) { onApplyDefaults(existingRecords); onBeforeUpdate(existingRecords);}

RE: 3, you could put this in the inner domain class constructor class at the bottom of your domain class perhaps? To encapsulate it better. Though if you are disabling all events it's arguably more efficient to just by-pass the domain handler in the trigger code completely. if(customsetting){ domainclass.getTriggerEvent(YourDomain.class).disableAll() } else { call domainclass here }

Hope this helps!

ab0369 commented 5 years ago

@afawcett

I really appreciate all the contributions you are making towards this apex world and thank you very much for responding.

RE: 1 - Do you mean create our own base classes similar to fflib_SObjectDomain by grouping the common functionality ?

RE: 2 - That's exactly what I ended up implementing

ab0369 commented 5 years ago

@stohn777 Thank you very much for taking time to answer my questions.

  1. We did chunk them into separate classes and methods and reviewed the strategy pattern as you suggested

  2. Implemented in beforeupdate method as @afawcett has described above

  3. That's a good option to create a Interface for triggers to implement this common functionality.

afawcett commented 5 years ago

RE: 1, yes thats what i ment and no that interface was just an example.

RE: 2, nice!

@ab0369 thanks for sharing your results for future readers, appreciated.\

@agarciaffdc i believe this can be closed out now.