8

As the Security model of Apex was leaky in the past, everybody who took - or as AppExchange Partners had to take - Security seriously was building or using libraries to enforce CRUD/FLS rules. The most notable among them is the FinancialForce FFLIB or Patron DmlManager.

When stripInaccessible(), WITH_SECURITY_ENFORCED and USER MODE where initially piloted, I already asked this question and there where many skeptics that they would finally end the need for clunky libraries.

But hey, isn't that exactly the goal of Salesforce? To make our and their lives easier when writing and reviewing Apex code.

So my Questions are:

  1. Why do you think we can / can't replace SOQL Builders with User Mode SOQL?
  2. Why do you think we can /can't replace DML Managers with User Mode DML?
12
  • 4
    This is, IMHO, an opinion based question and would be better addressed on the Salesforce forums. Take a look at How to Ask for more.
    – Phil W
    Commented Mar 21, 2023 at 12:25
  • 2
    That said.... In our package we often find we need to query records with certain essential fields but then with optional additional fields. Omitting the additional fields from the results is fine, but omitting the essential fields is not. Another way: the query should fail if essential fields are not accessible, but should succeed and simply omit optional details. Until this is supported we cannot use user mode directly.
    – Phil W
    Commented Mar 21, 2023 at 12:26
  • 1
    @PhilW the watchdog ;-) Your critisicm towards the last point is fair. I removed it. I must reject the opinion-based. This is THE best forum in Salesforce. Tell me where to else reach and store the most wisdom. Everything in the end is opinion based. Unless this is a Math solver. ;-) Please leave it open. Commented Mar 21, 2023 at 12:30
  • 1
    Robert, it's not a real answer, since it doesn't cover the whole question, but I felt it worth pointing out an existing gap/unsupported pattern with user mode. Chris and Daniel mentioned a "user mode with perm set" concept to plug this gap, though I've got some reservations around use of an admin modifiable perm set to cover fields/objects the user doesn't have access to. We have a lot of different queries of a lot of different objects for a lot of different use cases and adding perm sets for each would not make sense. Having a single extra perm set also doesn't make sense... it's a quandary
    – Phil W
    Commented Mar 21, 2023 at 12:35
  • 1
    With the user mode, Salesforce has made a big step towards simplification that can help those libraries to perform better and provide an improved development experience. However, it is up to you to decide if you want to decouple the DB operations and how the security is actually implemented from the business code. E.g. Selectors are aiming code reuse and encapsulation. That's not something you can simply replace by using the new user mode. Moreover, unless the stubbing API comes with a magic formula, these tools are great for building true robust unit tests Commented Mar 21, 2023 at 21:06

3 Answers 3

5

Why do you think we can / can't replace SOQL Builders with User Mode SOQL?

I will answer your first question because I have more experience with Selectors which gives me a better view of why we need them.

First of all WITH USER_MODE applies an FLS + Sharing rules to the SOQL:

The object permissions, field-level security, and sharing rules of the current user are enforced.

Of course, it's a good practice to do it, but we do not live in a perfect world, a lot of projects have bad design. What if I would like to respect FLS, but get records without sharing to meet business requirements? WITH USER_MODE is not always a solution.

Secondly WITH USER_MODE covers a tiny fraction (FLS, sharing rules*) of what Selectors can do.

There are a lot of benefits that the Selector layer can provide:

  1. Additional level of abstraction - The selector layer is an additional level of abstraction that gives you the possibility to control the execution of SQOL. It's a quite generic statement, but that level is a source of other benefits.
  2. Mocking - Selector classes give a possibility to mock return values in unit tests.
    • Mock external objects (__x) - External objects cannot be inserted in unit tests. You need to mock all SOQLs, of course, there is if (Test. isRunningTest()), but not an elegant and clean solution.
    • Mock custom metadata - Custom metadata cannot be inserted in unit tests unless the developer uses the Metadata API. Mocking can be a solution.
  3. Control field-level security - The best practice is to execute SOQLs WITH USER_MODE to enforce field-level security and object permissions of the running user. The selector layer can apply WITH USER_MODE by default to all of the queries, so the developer does not need to care about it. Developers can also add WITH SYSTEM_MODE to all SOQLs from a specific selector. I believe selectors make FLS control easier.
  4. Control sharings rules - The selector allows to execution of different SQOLs within the same class in different sharing modes (with sharing/without sharing).
  5. Avoid duplicates - Generic SQOLs like getById, and getByRecordType can be stored in the selector class. It makes developers' lives easier when you have predefined/tested methods.
  6. Default configuration - The selector class can provide default SOQL configuration like default fields, FLS settings, and sharing rules. I was on a project were sharing rules were screwed up and each SOQL needed a default condition OwnerId = :UserInfo.getUserId(), it was quite simple to achieve with Selectors by adding a default condition to all of the SOQL.
  7. Cache - The selector can have additional logic to save SOQL results. That approach can be beneficial in places where we need to save SOQL limits.

SOQL Lib

Resources:

2
  • "Of course, it's a good practice to do it, but we do not live in a perfect world, a lot of projects have bad design. What if I would like to respect FLS, but get records without sharing to meet business requirements? WITH USER_MODE is not always a solution." - You can use WITH SECURITY_ENFORCED instead of WITH USER_MODE here to restrict only to CRUD/FLS permissions without record level security. Commented Oct 30, 2023 at 13:36
  • 1
    Yes, sure @ŁukaszFranczyk, but Salesforce do not recommend it. Salesforce recommends that you enforce Field Level Security (FLS) by using WITH USER_MODE rather than WITH SECURITY-ENFORCED because of these additional advantages. More about it in: developer.salesforce.com/docs/atlas.en-us.apexcode.meta/… Commented Oct 30, 2023 at 14:10
3

Good answers already given for the advantages of selector layer. I think the selector layer is one level of abstraction above the SOQL builder, it can be used with native SOQL to achieve the same advantages without a SOQL builder. Selectors and builders are little different.

The advantages of SOQL builder itself can be:

  1. Enable reuse of SOQL statement fragments, such as where condition, and compose them with different criteria.
  2. When SOQL contains date and currency literals, we can only use dynamic SOQL with string concatenation to inject the values. This will fail: [SELECT Id FROM Account WHERE AnnualRavenel > USD:amount].
  3. With SOQL builder we can enhance its features, such as introduce new comparison operators like between(Account.AnnualRavenel, 100, 500).

The following demo built with ApexQuery library which supporting run(AccessLevel.USER_MODE) as well.

List<Id> accountIds = ... ;
String countryName = 'United State';
Decimal minAmount = 100;
Decimal maxAmount = 500;

List<Account> accounts = (List<Account>) Query.of(Account.SObjectType)
    .selectBy(Account.Name, Account.BillingCountry, Account.BillingState)
    .selectBy(FORMAT(CONVERT_CURRENCY(Account.AnnualRevenue)))
    .selectBy('Contacts', Query.of(Contact.SObjectType).selectBy(Contact.Name))
    .filterBy(andx()
        .add(inx(Account.Id, accountIds))
        .add(between(Account.AnnualRevenue, USD(minAmount), USD(maxAmount)))
        .add(eq(Account.BillingCountry, countryName))
    )
    .orderBy(Account.AnnualRevenue).descending().nullsLast()
    .run(AccessLevel.USER_MODE);

Resources:

2
  • Your lib indeed looks nice. But this question is not so much about "what is the best Soql Builder" but more do we need them with USER Mode anymore. Can you answer that and maybe show how and where your lib is incorporating Salesforce new native Security features? Commented Mar 22, 2023 at 11:10
  • 2
    Hey Robert, thanks for your feedback, ApexQuery supports user mode when execute with run(AccessLevel.USER_MODE), I have added it to the example. By default, it will run under system mode without the parameter.
    – Jeff Jin
    Commented Mar 22, 2023 at 11:27
2

Piotr's excellent answer covers all of the benefits of "SOQL builders". Like he says, their purpose is far more than just to enforce CRUD and FLS.

As for "DML Managers" -- there is still advantage to having a single point of abstraction for some of the same reasons (mocking, auditing/logging, Exception handling, etc.).

With regard to DMLManager -- the actual guts of DMLManager are due to be replaced with the new native Database.insert/update/delete that enforce CRUD/FLS. All of the heavy lifting that DMLManager did to detect "diffs" can now be removed to save CPU and the extra SOQL query. I just haven't gotten around to it yet :-)

2
  • So would you still need a DmlManager lib or could we just go without it? I mean why a custom Apex wrapper about native DML statements? Commented Mar 29, 2023 at 15:07
  • 1
    Abstraction :-) If you ever need to add some custom logging/Exception handling/etc, it's way easier to do it in one place than 9000
    – daveespo
    Commented Mar 29, 2023 at 19:10

You must log in to answer this question.

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