fflib-apex-common icon indicating copy to clipboard operation
fflib-apex-common copied to clipboard

Enterprise Patterns 'Light'

Open john-m opened this issue 10 years ago • 26 comments
trafficstars

Firstly - thanks for sharing the patterns!

This post was prompted by a brief exchange with Andrew after this tweet :

At the time I was trying to decide whether to use the patterns in my upcoming project but was concerned about the complexity. Hence, my interest in a ‘Light’ version…

We’ve been on Salesforce for about 18 months and our implementation, although not particularly large, covers quite a lot of the platform with two-way integrations to our website on the frontend and our legacy ERP on the backend, scheduled & batch Apex plus the usual VF controllers and triggers etc. At this stage I'm starting to feel the maintenance pain and therefore very receptive to the ‘separation of concerns’ approach.

I’m the business owner and spend only a small part of my time as a ‘developer’, going months (if all is running well) without touching the code… but we’re about to embark on another phase of implementation involving FinancialForce Accounts & HCM and then re-implementing what’s left of the ERP in Salesforce.

In short I want maximum separation of concerns to help with maintainability but with minimum complexity!

I've seen this post from Matt Lacey but I’m not confident enough to go pulling the framework apart and I have a feeling that I’m going to find some of the more advanced parts, eg mocks, useful once I get into it, so my approach to 'Light' so far is to just pick what seem like the more straightforward and approachable parts and start there. Something like this :

  • Application class that just implements Unit of Work
  • Service classes - I’d already started collecting related methods into classes so this involves the adoption of a more formal and disciplined approach to this, and Unit of Work is helpful here
  • Selector classes - pretty straightforward to implement and address an issue that I’ve already experienced trouble with (consistency of fields returned via SOQL)
  • Domain class - this one seems less obvious but plan to try it out next time I need to code a trigger
  • Avoid most of the interfaces/factories/mocks ie the 'advanced' stuff, at least for the time being

I plan to try this with new code or when any of the existing code has to be reworked.

I’d be interested to hear how others have approached getting started and working with the patterns.

john-m avatar Mar 24 '15 20:03 john-m

Hi @john-m, thanks for the post!

Yes very keen to keep exploring with Matt on the light edition. Matt and I are keeping an an eye on it becoming a separate code base so want to find a way to update the main repo with a core version allowing extensions for features like mocks for example. Doing this so others using it already can have a clean upgrade path is still something we are pondering.

In the meantime your choices on what to use are perfect, the library is in fact quite modular, though Salesforce don't really help us realise this physically at the moment. You may want to take a closer look at the Unit Of Work, it really helps with bulkification and reduces code complexity when 'stitching' records together. Matt's original motivation was to keep down the 'footprint' as he is working in a consulting context and likely deploying raw into production orgs. In a packaged context, if your not worried about that and want to focus on only 'using' specific classes you want and just ignoring those you don't that also works just fine.

Thanks for taking the time to post and share your thoughts! :+1:

afawcett avatar Mar 26 '15 08:03 afawcett

We have been using some aspects of Enterprise Patterns since the summer of 2014 in our product. We have a large, complex codebase, and have been gradually incorporating Enterprise Patterns. At this time, we're primarily using the Selector pattern and the Unit of Work pattern within our application, but also making some use of the Domain pattern too. I think the Selector and Unit Of Work patterns are the two patterns that are most easily integrated into existing codebases.

In the case of the Selector pattern, what we've done is replace a lot of the static SOQL statements sprinkled throughout or code so that we are now utilizing classes that extend SObjectSelector. We find that in many cases, when we need to retrieve a set of records, in addition to pulling in the base fields directly from the SObject, we also need to pull related fields from the parent SObject(s). What we've done is extend the base SObjectSelector with a class that exposes a "getRelatedFieldList" method. This makes it easier for one SObjectSelector class to add related fields from a parent SObjectSelector. For example, if I had a Selector_Contact class, it could invoke the getRelatedFieldList on the Selector_Account class to add the related fields from the parent Account to the Contact queries it generates.

We use the Unit Of Work pattern pretty extensively too. We use it a lot in the scaffolding of objects for unit testing purposes. Our unit tests often require extensive scaffolding, with the creation of multiple layers of inter-related objects before the test can even be executed (one of these days, I need to devote some time to looking more closely at Apex Mocks). UOW makes for much cleaner, tighter scaffolding code. We also utilize it pretty extensively in some of the more complex pieces of business logic, where we're modifying collections of inter-related objects in different ways (for example, applying discounts, then calculating tax, then applying fees). We also integrated UOW with our CRUD/FLS security implementation, so that the "commitWork" operation delegates to our CRUD/FLS layer when performing the actual underlying DML operations.

We also make some use of the Domain pattern. Our system is an order processing system, whereby there is an order, plus any number of child line items. We implemented a Domain class to wrap these child line items. This Domain class contains the business logic for actually performing the operations such as applying discounts, or calculating tax, etc., on a collection of line items.

Hope this helps you get started within integrating Enterprise Patterns into your applications.

tfuda avatar Mar 26 '15 11:03 tfuda

Awesome contribution to this thread @tfuda thank you!

afawcett avatar Mar 26 '15 18:03 afawcett

(Disclaimer: @tfuda and I work together)

I also want to chime up in support Matt Lacey's blog post -- We're actually a few months behind on taking the upstream updates to fflib-apex-common due to all of the Apex Mocks code that is now a compile-time dependency -- it's kind of frightening to pull in 30 or 40 classes before you even write a line of code (this is more of a indictment of the lack of 'package' or external library support in Apex, not of Apex Mocks or the Enterprise Patterns)

So basically, this is a +1 comment :-)

daveespo avatar Mar 26 '15 21:03 daveespo

Thanks for the comments everyone.

@tfuda getRelatedFieldList() on SObjectSelector sounds like a great idea! Your comments about the Domain class are interesting - I have a similar situation with Contracts & Contract 'Lines' to implement so I'll take a look at that.

Agree that the number of classes is a bit overwhelming at first and finding a manageable way to approach it has been a challenge. The very small amount I've done so far - a 'Service' using UoW went very smoothly :)

john-m avatar Mar 27 '15 08:03 john-m

The integration with fflib_QueryFactory and fflib_SObjectSelector does permit selector parent child reuse through the approach shown in this exampe. By call addQueryFactorySubselect method. You can also configureQueryFactoryFields to add lookup relationship fields to the query.

public List<Opportunity> selectByIdWithProducts(Set<ID> idSet)
{
    fflib_QueryFactory opportunitiesQueryFactory = newQueryFactory();

    fflib_QueryFactory lineItemsQueryFactory = 
        new OpportunityLineItemsSelector().
            addQueryFactorySubselect(opportunitiesQueryFactory);

    new PricebookEntriesSelector().
        configureQueryFactoryFields(lineItemsQueryFactory, 'PricebookEntry');
    new ProductsSelector().
        configureQueryFactoryFields(lineItemsQueryFactory, 'PricebookEntry.Product2');
    new PricebooksSelector().
        configureQueryFactoryFields(lineItemsQueryFactory, 'PricebookEntry.Pricebook2');

    return (List<Opportunity>) Database.query(
        opportunitiesQueryFactory.setCondition('id in :idSet').toSOQL());
}

This results in the following SOQL...

SELECT CloseDate, ExpectedRevenue, Type, StageName, Pricebook2Id, Id, Description, Amount, AccountId, Name, DiscountType__c, Probability,  
   (SELECT PricebookEntry.Product2Id, PricebookEntry.Pricebook2.Description,
    PricebookEntry.UseStandardPrice, PricebookEntry.UnitPrice, Id, PricebookEntry.Id,
    PricebookEntry.Product2.Name, SortOrder, PricebookEntry.Pricebook2.Id,
    PricebookEntry.Pricebook2.IsStandard, PricebookEntry.Name, TotalPrice, PricebookEntryId,
    PricebookEntry.Pricebook2.IsActive, OpportunityId, PricebookEntry.Product2.Description,
    PricebookEntry.Product2.IsActive, PricebookEntry.Pricebook2.Name, Quantity, UnitPrice,
    PricebookEntry.IsActive, PricebookEntry.Pricebook2Id, PricebookEntry.Product2.ProductCode,
    Description, PricebookEntry.ProductCode, PricebookEntry.Product2.Id, ListPrice, 
    PricebookEntry.Product2.DiscountingApproved__c FROM OpportunityLineItems ORDER BY
    SortOrder ASC NULLS FIRST , PricebookEntry.Name ASC NULLS FIRST )
    FROM Opportunity WHERE id in :idSet ORDER BY Name ASC NULLS FIRST 

afawcett avatar Mar 27 '15 15:03 afawcett

Thanks @afawcett !

john-m avatar Mar 27 '15 18:03 john-m

Just a +1 from me to say, i've not forgotten about this thread and the great work Matt has started.

afawcett avatar Jun 06 '15 09:06 afawcett

Heh, I had no idea this discussion had even started! Nice :+1:

I'm definitely interested in seeing how we can get the changes I made merged back in like we talked about on the phone so many months ago (seriously, where does time go?). The stripped back version I created has worked really well for one of our clients so I think it has merit, finding free time is another matter of course.

mattlacey avatar Jul 27 '15 13:07 mattlacey

@mattlacey i hear ya, i know, weeks are spinning by for me, Dreamforce feels like it could be next week and i've so much to do! But yes, i am also pleased the conversation is alive and the desire is as well, it would just take a strong window of effort to crack it i'm sure, but we will, confident in that i am! :+1:

afawcett avatar Jul 27 '15 18:07 afawcett

I'm looking at moving a client project of mine into the "light" branch that @mattlacey built out. So, a general +1 for this idea.

cdcarter avatar Sep 11 '15 17:09 cdcarter

Excellent @cdcarter post Dreamforce when my world goes back standard level of busy, i'll for sure wanting to be looking at this! :+1:

afawcett avatar Sep 12 '15 09:09 afawcett

I'm also interested in 'light edition', wondering how outdated it is from the original fflib-apex-common. @mattlacey, would it be possible to have instructions on how to remove the dependency on the Apex Mocks library? P.S. Was googling for 'best practices' and frameworks for force.com development and this turned out to be the most thought concept, along with some other triggers and test 'factory' solutions available from other developers. It's such a shame for the company with billions in revenue, thousands of employees and certified developers, hundreds of platform evangelists and MVPs to have outdated and broken links on the help documentation and even more outdated code shares and who-knows-why so called 'best practice' articles, and to do not have at least a few open source community supported common utilities code repositories available.

o-lexi avatar Dec 12 '15 20:12 o-lexi

I'm hoping to get stuck into this in the new year for sure!

afawcett avatar Dec 12 '15 23:12 afawcett

Thank you, Andrew! I just compared some of the classes and, considering a larger user base (I hope ;)), continuous fixes, and not that many classes in the Apex Mocks library, it would be beneficial to start with FinancialForce Apex Common

o-lexi avatar Dec 13 '15 00:12 o-lexi

#notforgotten

afawcett avatar Mar 22 '16 23:03 afawcett

Whoah, where did the time go? Sorry for not replying @o-lexi, was travelling in December and just missed it :(

I've not updated my code in a while so it's probably lagging, but then it mostly consists of 'core' features only which don't change as much. Might try and review my fork over the next week or two.

mattlacey avatar Mar 22 '16 23:03 mattlacey

First of all, thanks for the patterns, the "light" fork, and for this thread!

I'd love to use the light fflib version, but for reason that @o-lexi mentions, feel like it's safer to go with the heavy version.

@cdcarter , @mattlacey , are you using the light fork and happy with it?

pyao-bwc avatar Jan 09 '19 19:01 pyao-bwc

It' still in use in one of the AppExchange apps we support, but hasn't been updated in a long time. Basically it works well enough that it's not needed changes.

For the main code base I work on it wasn't a good fit as that has a rather unique architecture, but I do use a lot of the ideas and patterns, just reimplemented in a different way.

mattlacey avatar Jan 09 '19 21:01 mattlacey

Thanks for the data point and your contributions to the community, Matt! Long time reader of your blog.

pyao-bwc avatar Jan 11 '19 14:01 pyao-bwc

Thanks, appreciate that! My blog's died off a little in the last year or two, going to try and make more of an effort but a lot of the stuff I'm doing these days doesn't revolve around the cutting edge of the platform!

On Sat, 12 Jan 2019 at 01:45, pyao-bwc [email protected] wrote:

Thanks for the data point and your contributions to the community, Matt! Long time reader of your blog.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/financialforcedev/fflib-apex-common/issues/36#issuecomment-453539648, or mute the thread https://github.com/notifications/unsubscribe-auth/ABMrW4lGLeqeTP-SzP2I-m063ybunKRwks5vCKOigaJpZM4D0ExJ .

mattlacey avatar Jan 13 '19 23:01 mattlacey

Is there a 'light' fork/version? if so where do I see it? thanks all

marktroupe avatar Apr 10 '19 06:04 marktroupe

I forked it a few years ago (https://github.com/mattlacey/fflib-apex-common) but never kept it up to date with the main version. I used it for a specific product which hasn't needed an update since!

On Wed, 10 Apr 2019 at 16:54, Mark Troupe [email protected] wrote:

Is there a 'light' fork/version? if so where do I see it? thanks all

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/financialforcedev/fflib-apex-common/issues/36#issuecomment-481558611, or mute the thread https://github.com/notifications/unsubscribe-auth/ABMrW_btfQ6VGgtJPYcXvUJ4oV80qVWOks5vfYqNgaJpZM4D0ExJ .

mattlacey avatar Apr 10 '19 07:04 mattlacey

thanks @mattlacey

marktroupe avatar Apr 11 '19 04:04 marktroupe

@mattlacey Thanks for the fork. Was fflib_Application available when you forked? Seems also like a simple addition.

gotstu avatar May 03 '19 17:05 gotstu

I don't believe it was. Feel free to update & expand!

On Sat, 4 May 2019 at 03:27, gotstu [email protected] wrote:

@mattlacey https://github.com/mattlacey Thanks for the fork. Was fflib_Application available when you forked? Seems also like a simple addition.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/financialforcedev/fflib-apex-common/issues/36#issuecomment-489175350, or mute the thread https://github.com/notifications/unsubscribe-auth/AAJSWWZOOB5RUDZJ6NDNLGTPTRYW3ANCNFSM4A6QJREQ .

mattlacey avatar May 06 '19 00:05 mattlacey