trailhead

Learn Domain Layer Principles

Learning Objectives

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

  • Summarize the origins of the Domain pattern from Martin Fowler’s 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.

Introduction

Before tackling this module, it’s important to complete the Apex Enterprise Patterns: Service Layer module. In that module, we discussed the Service layer as a means to encapsulate your application’s business processes, focusing on how services are exposed in a consistent, meaningful, and supportive way to other parts your application, such as Visualforce controllers, Batch Apex, and public-facing APIs.

This unit deals with a layer in your application known as the 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"

With Force.com, 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 Force.com 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 relating to objects within your application requires Apex coding, consider structuring and factoring the code using object-oriented programming (or 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.” Martin Fowler, EAA Patterns

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 a Force.com perspective on the Domain Model pattern. It provides guidelines to clearly associate 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 and Apex service method call introduced in the previous 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.

Sharing domain logic

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

Design Considerations

Essentially, 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.

  • Bulkification - As with the Service layer, bulkification is a key design consideration on Force.com and has bearing on the domain layer’s design guidelines to ensure that domain class constructors, parameters, methods, and properties deal with data in terms of lists and not singular instances. This approach helps promote, and thus propagate further down the layers, this critical best practice more visibly. After all, no point in having a bulkified service if the rest of your application code does not support it.

  • Extension by containment - Domain logic must encapsulate both data and behavior. Apex represents data as sObjects. You can’t extend an sObject, but you can wrap it or contain it with another class that complements the data with appropriate behavioral code (methods, properties, and so on).

  • Naming conventions - Because the domain data is a data set or list as per the bulkification convention, your domain class ideally reflects this in its name, for example, public class Invoices.

  • OOP - In Force.com, one custom object cannot extend another in the same way classes do in OOP languages such as Java. Therefore, you often end up creating a number of custom objects that share similar aspects, sets of fields, and relationships. When defining your Domain classes for such objects, consider using inheritance or interfaces to abstract common behavioral aspects into a shared base class or interface, allowing you to create and reuse logic that applies across such objects. This unit uses some aspects of OOP. Ideally, you are familiar with some of the basics, such as inheritance and method overriding.

  • Security - Avoid using the with sharing or without sharing keywords on Domain Apex classes to ensure that the calling context drives this aspect. Typically, this is the Service class logic that, as per its design guidelines, must specify with sharing.

  • Separation of Concerns - Be consistent regarding where common types of domain logic, such as validation, defaulting, and calculations go. While it is possible to mix this in with other Domain logic solely related to database and trigger events, you have the option to split this logic across different methods in the Domain class. Consider if it is useful to expose this code to make it accessible in contexts not driven by database operations., such as services that provide validation or defaulting-only behavior.

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 are going to 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 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 previous diagram, 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 is 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

retargeting