Skip to main content

Learn Domain Layer Principles

Learning Objectives

After completing this unit, you’ll be able to:

  • Summarize the origins of the Domain pattern using EAA patterns.
  • Determine which type of Apex code belongs in the Domain layer.
  • Explain where the Domain layer fits within your application architecture and the platform.
  • Design a Domain layer to work within the platform’s best practices.

Prerequisites

Welcome! This module builds on principles you learned in the Apex Enterprise Patterns: Service Layer module, and steps you completed in your Trailhead playground during the hands-on challenges. In that module, we discussed the Service layer as a means to encapsulate your application’s business processes, and focused on how services are exposed in a consistent, meaningful way to other parts of your application, such as Visualforce controllers, Batch Apex, and public-facing APIs.

If you haven’t taken the Apex Enterprise Patterns: Service Layer module, complete it before continuing.

Domain Layer

Domain (software engineering) - “A domain is a field of study that defines a set of common requirements, terminology, and functionality for any software program constructed to solve a problem in the area of computer programming, known as domain engineering.” —Wikipedia, Domain (software engineering)

With Salesforce Platform Enterprise Patterns, the domain layer is defined through the custom objects you create (project, invoice, and so on), allowing you to rapidly build the data storage of your application. But what about the behavior of those objects, such as validation, calculation, and the complex manipulation of data? You can express behavior on Salesforce both declaratively (point-and-click) and programmatically using Apex. Using this combination effectively is key to being a successful application developer on the platform. And, honestly, don't we all want to be successful developers on the platform?

Domain Pattern

If the complexity of certain behaviors for objects within your application requires Apex coding, consider structuring and factoring the code using object-oriented programming (OOP) techniques and the Domain Model pattern.

Domain Model - “An object model of the domain that incorporates both behavior and data." “At its worst business logic can be very complex. Rules and logic describe many different cases and slants of behavior, and it's this complexity that objects were designed to work with.” —Fowler, Martin

Like the Service layer, the Domain Model provides a more granular level of code encapsulation and reuse within your application, such as complex validation, defaulting, and other logic relating to complex calculation and manipulation.

This unit takes an Apex specific perspective on the Domain Model pattern. It provides guidelines to clearly associate the Domain layer code to each custom object, enabling you to further manage effective layering and Separation of Concerns (SOC) within your application code base. Before we get into what constitutes a Domain class, let’s review where they are used.

  • Apex triggers: Create, read, update, and delete (CRUD) operations, including undelete, occur on your custom objects as users or tools interact via the standard Salesforce UIs or one of the platform’s APIs. The operations are routed to the appropriate Domain class code corresponding to that object and operation.
  • Apex services: Service layer code should be easy to identify and make it easy to reuse code relating to one or more of the objects that each of its operations interacts with via Domain classes. This approach keeps the code in the service layer focused on orchestrating whatever business process or task it is exposing.

The Apex Domain class is consumed by the Apex trigger handler logic and Apex service method call introduced in the Apex Enterprise Patterns: Service Layer module. This unit covers how these two areas can share logic exposed via a Domain class, either explicitly by calling its methods or indirectly via a trigger handler logic.

Sharing domain logic diagram.

Important: Remember that code relating to Lightning Web Component controllers, Visualforce controllers, Batch Apex, and API classes that you develop should use functionality solely exposed via the Service layer operations. Thus, code written in the Domain layer is always indirectly invoked by service tier client classes. Domain layer code is typically an internal business logic aspect of your application.

Design Considerations

The Domain layer opens up OOP capabilities within Apex to your application but does so in a way that is aligned (mostly by naming conventions) with your application’s domain terms and concepts and the platform’s own best practices.

Here are some design considerations to think about. 

Follow Along with Trail Together

Want to follow along with an expert as you work through this step? Take a look at this video, part of the Trail Together series.

Using Domain Classes from Services

Let's take a look at the OpportunitiesService introduced in the Apex Enterprise Patterns: Service Layer module to refactor some of the applyDiscounts method behavior further down into a Domain class method.

The following code shows a new implementation of the Service method applyDiscounts. This time it’s using the new Opportunities class we create in this unit. This class is encapsulating the logic that performs the discount calculations via its own applyDiscount method.

public static void applyDiscounts(Set<Id> opportunityIds, Decimal discountPercentage) {
    // Unit of Work
    // …

    // Validate parameters
    // ...

    // Construct Opportunities domain class
    Opportunities opportunities =
        new Opportunities(
            [select Id,Amount, (select UnitPrice from OpportunityLineItems)
            from Opportunity where Id in :opportunityIds]);

    // Apply discount via domain class behavior
    opportunities.applyDiscount(discountPercentage, uow);

    // Commit updates to opportunities     
    uow.commitWork();                      
}

Using Domain Classes from Triggers

Let’s also consider another entry point. As illustrated in the diagram in the Domain Pattern section, users invoke Apex triggers via the Salesforce UIs or code manipulating the records via DML or Salesforce APIs.

Given the role of the Domain class in respect to encapsulating all logic and behavior for a given Domain object, we need to ensure that Apex trigger calls are also routed to the appropriate class methods.

As with many emerging patterns around Apex triggers, it’s good to keep the logic minimal in the trigger. Apex triggers are not classes and have limited means for factoring code or using OO principles like inheritance. They make poor places for even moderately complex logic.

The following Apex trigger code is made possible given the use of the fflib_SObjectDomain class which is part of the Apex Enterprise Patterns open source library. This class forms a base class for all the Domain classes in your application. More on this in the next unit!

The static method fflib_SObjectDomain.triggerHandler ensures that the applicable method in the Opportunities class is called by reading the system Trigger context variables, Trigger.isAfter, Trigger.isBefore, Trigger.isInsert, Trigger.new and so on. The code that goes into Apex triggers using this pattern is intentionally very light.

trigger OpportunitiesTrigger on Opportunity (
  after delete, after insert, after update, after undelete, before delete, before insert, before update) {
   // Creates Domain class instance and calls appropriate methods
   fflib_SObjectDomain.triggerHandler(Opportunities.class);
}

The Opportunities class referenced in the above code contains methods such as onBeforeInsert and onAfterUpdate, which implement the behavior of this object when these operations occur.

Resources

Share your Trailhead feedback over on Salesforce Help.

We'd love to hear about your experience with Trailhead - you can now access the new feedback form anytime from the Salesforce Help site.

Learn More Continue to Share Feedback