As discussed in the previous chapter, by default, Apex code runs in system mode, meaning no sharing rules are enforced. However, business logic behavior should, in general, honor sharing rules. To avoid sharing information to which the user does not have access, sharing rule enforcement must be a concern of the Service layer.
The Salesforce security review requires Apex controller class entry points to honor this, although your Service layer will be called by these classes and thus could inherit this context. Keep in mind that your Service layer is effectively an entry point for other points of access and integrations (as we will explore in a later chapter and throughout the book).
Thus, the default concern of the Service layer should be to enforce sharing rules. Code implemented within the Service layer or called by it should inherit this using the inherited sharing keyword. Code should only be elevated to running in a context where sharing rules are ignored when required, otherwise known as the without sharing context. This would be in cases where the service is working on records on behalf of the user. For example, a service might calculate or summarize some race data but some of the raw race data records (from other races) may not be visible to the user.
To enforce sharing rules by default within a Service layer, the with sharing keyword is used on the class definition, as follows:
public with sharing class RaceService
Other Apex classes you create, including those we will go on to discuss around Selector and Domain patterns, should use the inherited sharing modifier such that they inherit the context—this allows them to be reused in either context more easily.
If a without sharing context is needed, a private inner class approach, as shown in the following example, can be used to temporarily elevate the execution context to process queries or DML operations in this mode:
// Class used by the Service layer public inherited sharing class SomeOtherClass { // This method inherits sharing context from Service public static void someMethod { // Do some work in inherited context // ... // Need to do some queries or updates in elevated context new ElevatedContext().restOfTheWork(workToDo); } private void restOfTheWork(List<SomeWork>workToDo) { // Additional work performed by this class // ... } private without sharing class ElevatedContext { public void restOfTheWork(List<SomeWork>workToDo) { // Do some work in a elevated (without sharing) context SomeOtherClass.restOfWork(workToDo); } } }
Note that you can consider making the ability to run logic a parameter of your Service layer if you feel certain callers will want to disable this enforcement. The preceding code sample could be adapted to conditionally execute the restOfWork method directly or via the ElevatedContext inner class in this case.