<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rohit Maharashi</title>
    <description>The latest articles on DEV Community by Rohit Maharashi (@rohit_maharashi_a25b8c6b6).</description>
    <link>https://dev.to/rohit_maharashi_a25b8c6b6</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3295992%2F8e521561-2490-47e7-8def-0c8bdac0d84b.jpg</url>
      <title>DEV Community: Rohit Maharashi</title>
      <link>https://dev.to/rohit_maharashi_a25b8c6b6</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rohit_maharashi_a25b8c6b6"/>
    <language>en</language>
    <item>
      <title>Salesforce Rules Inference Engine – Version 1 (Field-Optimized Modular Approach)</title>
      <dc:creator>Rohit Maharashi</dc:creator>
      <pubDate>Sat, 23 Aug 2025 05:32:27 +0000</pubDate>
      <link>https://dev.to/rohit_maharashi_a25b8c6b6/salesforce-rules-inference-engine-version-1-field-optimized-modular-approach-1o0n</link>
      <guid>https://dev.to/rohit_maharashi_a25b8c6b6/salesforce-rules-inference-engine-version-1-field-optimized-modular-approach-1o0n</guid>
      <description>&lt;p&gt;This version of the rules engine is designed for clarity, modularity, and maintainability. It evaluates rules against SObject records in a scalable, bulkified manner, with optimizations to limit redundant field access and criteria evaluation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Process &amp;amp; Architecture&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;High-Level Flow&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Load Rules &amp;amp; Criteria: Retrieve all active rules and their associated criteria for the target object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Organize by Field: Group criteria by the field they reference, so each field is only accessed once per record.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For Each Record: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For each relevant field, evaluate all associated criteria.&lt;/li&gt;
&lt;li&gt;Store the result of each criterion (true/false).&lt;/li&gt;
&lt;li&gt;For each rule, substitute the results into the rule’s logical expression (e.g., 1 AND (2 OR 3)).&lt;/li&gt;
&lt;li&gt;If the rule expression evaluates to true, collect the rule as a match for that record.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Return Results: Output a list of matches (record, rule, and optionally actions).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Components &amp;amp; Code Snippets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A. RuleEngine (Entry Point)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class RuleEngine {
    public static List&amp;lt;RuleMatchResult&amp;gt; evaluate(List&amp;lt;SObject&amp;gt; records, String targetObjectApiName) {
        RuleRepository repo = new RuleRepository();
        RuleExecutionContext context = repo.loadContext(targetObjectApiName);
        List&amp;lt;RuleMatchResult&amp;gt; results = new List&amp;lt;RuleMatchResult&amp;gt;();
        for (SObject record : records) {
            Map&amp;lt;Integer, Boolean&amp;gt; criteriaResults = CriteriaEvaluator.evaluate(record, context);
            for (RuleExpression ruleExpr : context.ruleExpressions) {
                Boolean matched = RuleEvaluator.evaluate(ruleExpr, criteriaResults);
                if (matched) {
                    results.add(new RuleMatchResult(record.Id, ruleExpr.ruleId));
                }
            }
        }
        return results;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;B. RuleRepository (Load &amp;amp; Organize Rules/Criteria)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class RuleRepository {
    public RuleExecutionContext loadContext(String targetObjectApiName) {
        // Query active rules
        List&amp;lt;Rule__c&amp;gt; rules = [SELECT Id, RecordType.name, Status__c, Rule_End_Date__c, Rule_Expression__c, Rule_Name__c, Rule_Start_Date__c, Where_Clause__c, Fulfillment_Type__c, Rule_Criteria__c, Transaction_Type__c, 
                               Transaction_Subtype__c, Transaction_Status__c FROM Rule__c 
                               WHERE Status__c = 'Active' AND RecordType.name = 'Fulfillment Rules Engine'];
        Set&amp;lt;Id&amp;gt; ruleIds = new Map&amp;lt;Id, Rule__c&amp;gt;(rules).keySet();
        // Query criteria and junctions
        List&amp;lt;Rule_Criteria__c&amp;gt; junctions = [SELECT Id, RecordType.Name, Rule__c, Criteria_Number__c, Condition__r.Field_Name__c, Condition__r.Operation_Type__c, Condition__r.Value__c, Condition__r.Condition_Signature__c, 
                                           Condition__r.Field_Type__c, Condition__r.SObject_Name__c, Negate_Condition__c 
                                           FROM Rule_Criteria__c where Rule__c IN  :ruleIds];
        RuleExecutionContext context = new RuleExecutionContext();
        for (Rule__c rule : rules) {
            context.ruleExpressions.add(new RuleExpression(rule.Id, rule.Rule_Expression__c));
        }
        for (Rule_Criteria__c jc : junctions) {
            if(!context.fieldToCriteria.containsKey(jc.Condition__r.Field_Name__c)) {
                context.fieldToCriteria.put(jc.Condition__r.Field_Name__c, new List&amp;lt;RuleCriteriaWrapper&amp;gt;());
            }
            context.fieldToCriteria.get(jc.Condition__r.Field_Name__c).add(new RuleCriteriaWrapper(jc.Rule__c, Integer.valueOf(jc.Criteria_Number__c), jc.Condition__r.Operation_Type__c, jc.Condition__r.Value__c));
        }
        return context;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C. RuleExecutionContext (Runtime Data)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class RuleExecutionContext {
    public Map&amp;lt;String, List&amp;lt;RuleCriteriaWrapper&amp;gt;&amp;gt; fieldToCriteria = new Map&amp;lt;String, List&amp;lt;RuleCriteriaWrapper&amp;gt;&amp;gt;();
    public List&amp;lt;RuleExpression&amp;gt; ruleExpressions = new List&amp;lt;RuleExpression&amp;gt;();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;D. CriteriaEvaluator (Evaluate All Criteria for a Record)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class CriteriaEvaluator {
    public static Map&amp;lt;Integer, Boolean&amp;gt; evaluate(SObject record, RuleExecutionContext context) {
        Map&amp;lt;Integer, Boolean&amp;gt; result = new Map&amp;lt;Integer, Boolean&amp;gt;();
        for (String field : context.fieldToCriteria.keySet()) {
            Object actual = record.get(field);
            for (RuleCriteriaWrapper rc : context.fieldToCriteria.get(field)) {
                Boolean passed = evaluateCriterion(actual, rc.operator, rc.value);
                result.put(rc.criteriaNumber, passed);
            }
        }
        return result;
    }
    private static Boolean evaluateCriterion(Object actualValue, String operator, String expectedValue) {
        if (actualValue == null) return false;
        String a = String.valueOf(actualValue);
        String b = expectedValue;
        if (operator == 'equal_to') return a == b;
        if (operator == '!=') return a != b;
        if (operator == '&amp;gt;') return Decimal.valueOf(a) &amp;gt; Decimal.valueOf(b);
        if (operator == '&amp;lt;') return Decimal.valueOf(a) &amp;lt; Decimal.valueOf(b);
        // Add more as needed
        return false;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E. RuleEvaluator &amp;amp; ExpressionResolver (Logical Expression Evaluation)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class RuleEvaluator {
    public static Boolean evaluate(RuleExpression expr, Map&amp;lt;Integer, Boolean&amp;gt; criteriaResults) {
        String resolved = ExpressionResolver.substitute(expr.expression, criteriaResults);
        return ExpressionResolver.evalBooleanExpression(resolved);
    }
}
public class ExpressionResolver {
    public static String substitute(String expr, Map&amp;lt;Integer, Boolean&amp;gt; results) {
        for (Integer key : results.keySet()) {
            expr = expr.replaceAll('\\b' + String.valueOf(key) + '\\b', String.valueOf(results.get(key)));
        }
        return expr;
    }
    public static Boolean evalBooleanExpression(String expr) {
        expr = expr.replaceAll('AND', '&amp;amp;&amp;amp;').replaceAll('OR', '||').replaceAll('NOT', '!');
        // Note: For production, use a robust parser here!
        return Boolean.valueOf(evalWithCustomParser(expr));
    }
    private static Boolean evalWithCustomParser(String expr) {
        // Placeholder: implement or use a library for complex logic
        return expr.contains('true') &amp;amp;&amp;amp; !expr.contains('false'); // naive
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;F. RuleMatchResult (Result Structure)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class RuleMatchResult {
    public Id recordId;
    public Id ruleId;
    public RuleMatchResult(Id recordId, Id ruleId) {
        this.recordId = recordId;
        this.ruleId = ruleId;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🧮 Time Complexity (Optimized)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tkgv6sidcd8gafqtu4u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tkgv6sidcd8gafqtu4u.png" alt=" " width="800" height="109"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where&lt;br&gt;
N = Number of records to evaluate&lt;br&gt;
R = Number of rules&lt;br&gt;
C = Average number of criteria per rule&lt;br&gt;
F = Number of distinct fields referenced across all rules&lt;br&gt;
K = Average number of criteria per field (i.e., how many criteria reference the same field)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modular &amp;amp; Maintainable: Each component is focused and testable.&lt;/li&gt;
&lt;li&gt;Bulkified: Supports processing lists of records efficiently.&lt;/li&gt;
&lt;li&gt;Field-Level Optimization: Only accesses each relevant field once per record, reducing SOQL and CPU usage.&lt;/li&gt;
&lt;li&gt;Extensible: Easy to add new operators, criteria types, or actions.&lt;/li&gt;
&lt;li&gt;Declarative Logic: Business logic is data-driven and manageable by admins.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No Cross-Record Memoization: If many records have the same field value, the same criteria may be evaluated repeatedly (less efficient for large, repetitive data sets).&lt;/li&gt;
&lt;li&gt;Expression Evaluation is Basic: Current boolean logic parser is simplistic; complex nested logic requires a more robust solution.&lt;/li&gt;
&lt;li&gt;No Early Rule Skipping: All rules are evaluated for all records, even if some could be skipped based on missing fields.&lt;/li&gt;
&lt;li&gt;Not RETE-Optimized: Does not share evaluation work across records as in advanced rule engines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Use&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Moderate Data Volumes: When the number of records and rules is manageable, and field values are highly variable.&lt;/li&gt;
&lt;li&gt;Simplicity &amp;amp; Maintainability: When code clarity and ease of maintenance are more important than maximum performance.&lt;/li&gt;
&lt;li&gt;Foundation for Future Optimization: Can be incrementally improved (e.g., add memoization, postfix parsing) as needs grow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Summary Table&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcaw5tmc21ce7cbnlecr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcaw5tmc21ce7cbnlecr5.png" alt=" " width="800" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
Version 1 of the Salesforce Rules Inference Engine provides a clean, maintainable, and scalable foundation for rules evaluation. It is well-suited for most business scenarios with moderate scale and complexity. For enterprise-scale or highly repetitive data, further optimizations (as in Version 2) may be warranted.&lt;/p&gt;

&lt;p&gt;For enhancements, consider adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Robust boolean expression parsing (postfix/infix parser)&lt;/li&gt;
&lt;li&gt;Cross-record memoization for shared field-value evaluations&lt;/li&gt;
&lt;li&gt;Early rule skipping based on required fields&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>salesforce</category>
      <category>ruleengine</category>
    </item>
    <item>
      <title>Building a Configurable Rules Engine on Salesforce: A Modern Approach to Business Logic</title>
      <dc:creator>Rohit Maharashi</dc:creator>
      <pubDate>Fri, 25 Jul 2025 04:15:16 +0000</pubDate>
      <link>https://dev.to/rohit_maharashi_a25b8c6b6/building-a-configurable-rules-engine-on-salesforce-a-modern-approach-to-business-logic-1eda</link>
      <guid>https://dev.to/rohit_maharashi_a25b8c6b6/building-a-configurable-rules-engine-on-salesforce-a-modern-approach-to-business-logic-1eda</guid>
      <description>&lt;p&gt;Modern business processes demand flexibility, configurability, and reusability in automating decisions, validations, and actions across domains like entitlement assignment, pricing, fulfillment, and compliance. However, traditional hard-coded logic in Salesforce (Apex triggers, workflows, etc.) is often difficult to maintain and adapt as business requirements evolve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Challenges&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lack of Flexibility&lt;/strong&gt;: Hard-coded rules require code changes for modifications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redundancy&lt;/strong&gt;: Similar logic is duplicated across objects and processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited Reusability&lt;/strong&gt;: Conditions and actions aren’t easily shared or reused.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business User Empowerment&lt;/strong&gt;: Non-technical users can’t easily manage or update business rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Design Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To address these challenges, we propose a generic, extensible rules engine built on Salesforce custom objects. This engine enables administrators to define, manage, and execute rules declaratively, supporting a wide range of business scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design Principles&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Configurability&lt;/strong&gt;: Business logic is defined via metadata, not code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;: Conditions (criteria) and actions are normalized and reusable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility&lt;/strong&gt;: The model supports new rule types, criteria, and actions without major redesign.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability&lt;/strong&gt;: Centralized rule management reduces duplication and errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Data Model &amp;amp; Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Objects Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule&lt;/strong&gt;: Represents a business rule to be evaluated and executed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rule Name&lt;/strong&gt;: Unique name of the rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Record Type&lt;/strong&gt;: Categorizes rules (e.g., Entitlement, Fulfillment).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Draft, Active, Inactive, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority&lt;/strong&gt;: Evaluation order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start/End Date&lt;/strong&gt;: Optional effective/expiration dates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: Rule description.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule Expression&lt;/strong&gt;: Logical expression (e.g., 1 AND (2 OR 3)).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where Clause&lt;/strong&gt;: SOQL-style condition (optional).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Criteria (Condition)&lt;/strong&gt;: Defines an atomic, reusable condition.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Criteria Name&lt;/strong&gt;: Name of the condition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Record Type&lt;/strong&gt;: Categorizes rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: What this condition checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field Name&lt;/strong&gt;: Field to evaluate (e.g., Asset.Product__r.Category).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operator&lt;/strong&gt;: =, !=, &amp;gt;, &amp;lt;, IN, CONTAINS, IS NULL, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value&lt;/strong&gt;: Value to compare against.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External Id&lt;/strong&gt;: Unique identifier (e.g., FieldName+Operator+Value).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule Action&lt;/strong&gt;: Defines what happens when a rule matches.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rule (Lookup)&lt;/strong&gt;: Parent rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Record Type&lt;/strong&gt;: Categorizes rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action Type&lt;/strong&gt;: Create Record, Update Field, Assign Entitlement, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action Parameter&lt;/strong&gt;: Parameter for the action (e.g., Fulfill Date).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule Criteria (Junction)&lt;/strong&gt;: Links rules to criteria, supporting complex logic and reusability.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rule (Lookup)&lt;/strong&gt;: The rule this criterion belongs to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Record Type&lt;/strong&gt;: Categorizes rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Criteria (Lookup)&lt;/strong&gt;: The reusable condition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Negate Condition&lt;/strong&gt;: Negate the condition (NOT logic).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Criteria Number&lt;/strong&gt;: For grouping/ordering in expressions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjtlaamn48io2dts0l752.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjtlaamn48io2dts0l752.png" alt="Entity Relationship Diagram for Rule Engine" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup &amp;amp; Administration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rule Management&lt;/strong&gt;: Business users can create, edit, and activate rules via a custom Lightning App.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Criteria Library&lt;/strong&gt;: Centralized management of reusable conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action Catalog&lt;/strong&gt;: Define and manage available actions (e.g., field updates, record creation).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation&lt;/strong&gt;: Ensure rules have at least one active criterion and action before activation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auditing&lt;/strong&gt;: Enable field history tracking for change management and compliance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Applications &amp;amp; Use Cases&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This rules engine can be applied across multiple business domains, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Entitlement Assignment&lt;/strong&gt;: Automatically assign entitlements based on asset/product attributes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing Rules&lt;/strong&gt;: Apply discounts or surcharges based on customer, product, or order criteria.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order Validation&lt;/strong&gt;: Enforce business policies before processing orders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fulfillment Routing&lt;/strong&gt;: Route requests to the correct team or system based on type/status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance Checks&lt;/strong&gt;: Ensure transactions meet regulatory or internal standards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example Flow&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trigger&lt;/strong&gt;: An event occurs (e.g., order created).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule Evaluation&lt;/strong&gt;: Active rules for the context are fetched and prioritized.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Criteria Check&lt;/strong&gt;: Each rule’s criteria are evaluated using the defined logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action Execution&lt;/strong&gt;: If criteria are met, the corresponding actions are executed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Extensibility &amp;amp; Future Enhancements&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Criteria Grouping&lt;/strong&gt;: Support for nested/complex logic (e.g., (A AND B) OR (C AND D)).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action Chaining&lt;/strong&gt;: Execute multiple actions in sequence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule Sets&lt;/strong&gt;: Group rules for batch processing or scenario-based evaluation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration&lt;/strong&gt;: Invoke external services or flows as actions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Benefits&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agility&lt;/strong&gt;: Rapidly adapt to changing business needs without code changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Centralized logic reduces errors and duplication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Empowerment&lt;/strong&gt;: Business users can manage rules directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Supports new rule types and actions as requirements grow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A configurable rules engine on Salesforce empowers organizations to automate complex business logic with minimal technical overhead, ensuring agility, consistency, and maintainability as business needs evolve.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have you implemented a rules engine in Salesforce or another platform? Share your experiences and thoughts in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>ruleengine</category>
    </item>
    <item>
      <title>Salesforce Apex Async Trigger Job Orchestration Framework</title>
      <dc:creator>Rohit Maharashi</dc:creator>
      <pubDate>Sat, 12 Jul 2025 09:25:26 +0000</pubDate>
      <link>https://dev.to/rohit_maharashi_a25b8c6b6/salesforce-apex-async-trigger-job-orchestration-framework-30fm</link>
      <guid>https://dev.to/rohit_maharashi_a25b8c6b6/salesforce-apex-async-trigger-job-orchestration-framework-30fm</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Salesforce triggers often require post-commit or heavy logic that exceeds synchronous limits. Queueable Apex is the preferred async solution, but Salesforce restricts you to one queueable enqueue per async context. This makes orchestrating complex, multi-step, or nested async logic challenging.&lt;/p&gt;

&lt;p&gt;This framework solves those challenges, letting you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run multiple jobs in a strict order (even with sub-chains)&lt;/li&gt;
&lt;li&gt;Support internal chaining (nested async flows)&lt;/li&gt;
&lt;li&gt;Control job execution order and enablement via metadata (CMDT)&lt;/li&gt;
&lt;li&gt;Reuse the pattern across any object or trigger&lt;/li&gt;
&lt;li&gt;Stay governor-safe and testable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture at a Glance
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trigger
  ↓
Trigger Handler
  ↓
AsyncWorkCollector.add(...)
  ↓
AsyncWorkCollector.flush()
  ↓
AsyncTriggerJobService.chainNextJob (entry point)
  ↓
  [JobA] → [JobB] → [JobC] → [JobC1] → [JobC2] → [JobD] → [JobE]
  (with support for sub-chains, e.g., C1→C2 as an internal chain)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1. Enum: AsyncTriggerJobType
&lt;/h2&gt;

&lt;p&gt;Defines all possible jobs in your orchestration, including sub-chain jobs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public enum AsyncTriggerJobType {
    A, B, C, C1, C2, D, E
    // Add more as needed
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Custom Metadata (CMDT) Setup
&lt;/h2&gt;

&lt;p&gt;Create a CMDT named Async_Trigger_Job_Configuration__mdt with fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: (matches enum value, e.g., C1)&lt;/li&gt;
&lt;li&gt;Sequence__c: (integer, controls order)&lt;/li&gt;
&lt;li&gt;Is_Enabled__c: (checkbox)&lt;/li&gt;
&lt;li&gt;ObjectGroup__c: (e.g., Order)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You need one record per job type. Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(Name, Sequence, Is_Enabled, ObjectGroup) -&amp;gt; (A, 1, TRUE, Order)&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Service: AsyncJobTypeToggleService
&lt;/h2&gt;

&lt;p&gt;Handles enablement and ordering using CMDT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class AsyncJobTypeToggleService {
    // Checks if a specific job type is enabled (for a given object group)
    public static Boolean isEnabled(
        AsyncTriggerJobType type,
        Map&amp;lt;String, Async_Trigger_Job_Configuration__mdt&amp;gt; configs,
        String objectGroup
    ) {
        Async_Trigger_Job_Configuration__mdt config = configs.get(type.name());
        return config != null &amp;amp;&amp;amp; config.Is_Enabled__c &amp;amp;&amp;amp; config.Object_Group__c == objectGroup;
    }
    // Returns a list of enabled job types, filtered by object group and ordered by sequence
    public static List&amp;lt;AsyncTriggerJobType&amp;gt; getOrderedEnabledTypes(
        Map&amp;lt;String, Async_Trigger_Job_Configuration__mdt&amp;gt; configs,
        Set&amp;lt;AsyncTriggerJobType&amp;gt; candidates,
        String objectGroup
    ) {
        List&amp;lt;AsyncTriggerJobType&amp;gt; enabled = new List&amp;lt;AsyncTriggerJobType&amp;gt;();
        for (AsyncTriggerJobType type : candidates) {
            Async_Trigger_Job_Configuration__mdt rec = configs.get(type.name());
            if (rec != null &amp;amp;&amp;amp; rec.Is_Enabled__c &amp;amp;&amp;amp; rec.Object_Group__c == objectGroup) {
                enabled.add(type);
            }
        }
        enabled.sort(new AsyncJobTypeMetaComparator(configs));
        return enabled;
    }
    // Comparator for sorting by Sequence__c
    public class AsyncJobTypeMetaComparator implements Comparator&amp;lt;AsyncTriggerJobType&amp;gt; {
        private Map&amp;lt;String, Async_Trigger_Job_Configuration__mdt&amp;gt; configurations;
        public AsyncJobTypeMetaComparator(Map&amp;lt;String, Async_Trigger_Job_Configuration__mdt&amp;gt; configurations) {
            this.configurations = configurations;
        }
        public Integer compare(AsyncTriggerJobType typeA, AsyncTriggerJobType typeB) {
            Async_Trigger_Job_Configuration__mdt recA = configurations.get(typeA.name());
            Async_Trigger_Job_Configuration__mdt recB = configurations.get(typeB.name());
            Integer seqA = recA != null ? Integer.valueOf(recA.Sequence__c) : null;
            Integer seqB = recB != null ? Integer.valueOf(recB.Sequence__c) : null;
            // If seqA is null, place it after seqB
            if (seqA == null) {
                return 1;
            }
            // If seqB is null, place seqA before seqB
            if (seqB == null) {
                return -1;
            }
            // Compare sequence values
            if (seqA &amp;lt; seqB) {
                return -1;
            } else if (seqA &amp;gt; seqB) {
                return 1;
            } else {
                return 0;
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Data Container: AsyncTriggerJobBundle
&lt;/h2&gt;

&lt;p&gt;Tracks the jobs, their order, and supports internal chaining.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public with sharing class AsyncTriggerJobBundle {
    private Map&amp;lt;AsyncTriggerJobType, Object&amp;gt; jobMap;
    private List&amp;lt;AsyncTriggerJobType&amp;gt; orderedTypes;
    private Integer currentIndex = 0;
    private AsyncTriggerJobBundle continuationBundle;
    public AsyncTriggerJobBundle(Map&amp;lt;AsyncTriggerJobType, Object&amp;gt; jobMap, String objectGroup) {
        this.jobMap = new Map&amp;lt;AsyncTriggerJobType, Object&amp;gt;(jobMap);
        Map&amp;lt;String, Async_Trigger_Job_Configuration__mdt&amp;gt; configs = Async_Trigger_Job_Configuration__mdt.getAll();
        this.orderedTypes = AsyncJobTypeToggleService.getOrderedEnabledTypes(
            configs,
            new Set&amp;lt;AsyncTriggerJobType&amp;gt;(jobMap.keySet()),
            objectGroup
        );
        this.currentIndex = 0;
    }
    public void setContinuationBundle(AsyncTriggerJobBundle bundle) { this.continuationBundle = bundle; }
    public AsyncTriggerJobBundle getContinuationBundle() { return this.continuationBundle; }
    public Boolean hasMoreJobs() { return jobMap != null &amp;amp;&amp;amp; currentIndex &amp;lt; orderedTypes.size(); }
    public AsyncTriggerJobType getNextJobType() { if (!hasMoreJobs()) return null; return orderedTypes[currentIndex++]; }
    public Object getPayloadFor(AsyncTriggerJobType type) { return jobMap.get(type); }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Collector: AsyncWorkCollector
&lt;/h2&gt;

&lt;p&gt;Gathers jobs during the trigger and starts the async chain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class AsyncWorkCollector {
    private static Map&amp;lt;AsyncTriggerJobType, Object&amp;gt; jobRegistry = new Map&amp;lt;AsyncTriggerJobType, Object&amp;gt;();
    private static Boolean flushed = false;
    public static void add(AsyncTriggerJobType jobType, Object payload, String objectGroup) {
        // ...enablement check...
        jobRegistry.put(jobType, payload);
    }
    public static void flush(String objectGroup) {
        if (flushed || jobRegistry.isEmpty()) return;
        flushed = true;
        AsyncTriggerJobBundle bundle = new AsyncTriggerJobBundle(jobRegistry, objectGroup);
        AsyncTriggerJobService.chainNextJob(bundle, objectGroup);
    }
    public static void reset() {
        jobRegistry.clear();
        flushed = false;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Chaining &amp;amp; Orchestration: AsyncTriggerJobService
&lt;/h2&gt;

&lt;p&gt;Encapsulates chaining logic and internal sub-chain orchestration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class AsyncTriggerJobService {
    public static void chainNextJob(AsyncTriggerJobBundle bundle, String objectGroup) {
        if (bundle != null &amp;amp;&amp;amp; bundle.hasMoreJobs()) {
            AsyncTriggerJobType nextType = bundle.getNextJobType();
            Object nextPayload = bundle.getPayloadFor(nextType);
            Queueable nextJob = AsyncTriggerJobFactory.createJob(nextType, nextPayload, bundle, objectGroup);
            if (nextJob != null) {
                System.enqueueJob(nextJob);
            }
        } else if (bundle != null &amp;amp;&amp;amp; bundle.getContinuationBundle() != null) {
            chainNextJob(bundle.getContinuationBundle(), objectGroup);
        }
    }
    public static void startInternalChain(
        Map&amp;lt;AsyncTriggerJobType, Object&amp;gt; subJobs,
        String objectGroup,
        AsyncTriggerJobBundle mainChainBundle
    ) {
        AsyncTriggerJobBundle subBundle = new AsyncTriggerJobBundle(subJobs, objectGroup);
        subBundle.setContinuationBundle(mainChainBundle);
        chainNextJob(subBundle, objectGroup);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Job Factory
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AsyncTriggerJobFactory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class AsyncTriggerJobFactory {
    public static Queueable createJob(
        AsyncTriggerJobType type,
        Object payload,
        AsyncTriggerJobBundle bundle,
        String objectGroup
    ) {
        if (objectGroup == 'Order') {
            return OrderAsyncJobFactory.create(type, payload, bundle);
        }
        return null;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;OrderAsyncJobFactory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class OrderAsyncJobFactory {
    public static Queueable create(
        AsyncTriggerJobType type,
        Object payload,
        AsyncTriggerJobBundle bundle
    ) {
        String typeName = type.name();
        if (typeName == 'A') return new JobAQueueable(payload, bundle);
        if (typeName == 'B') return new JobBQueueable(payload, bundle);
        if (typeName == 'C') return new JobCQueueable(payload, bundle);
        if (typeName == 'C1') return new JobC1Queueable(payload, bundle);
        if (typeName == 'C2') return new JobC2Queueable(payload, bundle);
        if (typeName == 'D') return new JobDQueueable(payload, bundle);
        if (typeName == 'E') return new JobEQueueable(payload, bundle);
        return null;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Example Job Classes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JobCQueueable (starts a sub-chain):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JobCQueueable implements Queueable {
    private Object payload;
    private AsyncTriggerJobBundle jobBundle;
    public JobCQueueable(Object payload, AsyncTriggerJobBundle bundle) {
        this.payload = payload;
        this.jobBundle = bundle;
    }
    public void execute(QueueableContext context) {
        Boolean internalChained = false;
        // ... C's work ...
        Map&amp;lt;AsyncTriggerJobType, Object&amp;gt; subJobs = new Map&amp;lt;AsyncTriggerJobType, Object&amp;gt;{
            AsyncTriggerJobType.C1 =&amp;gt; null,
            AsyncTriggerJobType.C2 =&amp;gt; null
        };
        internalChained = true;
        AsyncTriggerJobService.startInternalChain(
            subJobs,
            'Order',
            this.jobBundle
        );
        // Do NOT call chainNextJob here; sub-chain will resume main chain!
        if(!internalChained) {
            AsyncTriggerJobService.chainNextJob(jobBundle, 'Order');
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;JobC1Queueable and JobC2Queueable:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JobC1Queueable implements Queuable{
    private Object payload;
    private AsyncTriggerJobBundle jobBundle;
    public JobC1Queueable(Object payload, AsyncTriggerJobBundle bundle) {
        this.payload = payload;
        this.jobBundle = bundle;
    }
    public void execute(QueueableContext context) {
        // ... C1's work ...
        AsyncTriggerJobService.chainNextJob(jobBundle, 'Order');
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Same pattern for JobC2Queueable.)&lt;/p&gt;

&lt;h2&gt;
  
  
  9. How Internal Chaining Works
&lt;/h2&gt;

&lt;p&gt;JobCQueueable creates a sub-bundle for C1 and C2, sets its continuation to the main bundle, and enqueues the dispatcher for the sub-bundle.&lt;/p&gt;

&lt;p&gt;The dispatcher runs C1, then C2.&lt;/p&gt;

&lt;p&gt;After C2, the dispatcher sees the sub-bundle is done, checks for a continuation bundle, and resumes the main chain (D, E, etc).&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Trigger Handler Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class OrderTriggerHandler {
    public static void afterInsert(List&amp;lt;Order&amp;gt; orders) {
        AsyncWorkCollector.add(AsyncTriggerJobType.A, orders);
        AsyncWorkCollector.add(AsyncTriggerJobType.B, orders);
        AsyncWorkCollector.add(AsyncTriggerJobType.C, orders);
        // ... add more as needed
        AsyncWorkCollector.flush('Order');
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Declarative: Admins can enable/disable jobs and set order in metadata.&lt;/li&gt;
&lt;li&gt;Governor-safe: Only one queueable enqueued per async context.&lt;/li&gt;
&lt;li&gt;Supports internal chaining: Sub-chains can run before resuming the main chain.&lt;/li&gt;
&lt;li&gt;Reusable: Works for any object or trigger.&lt;/li&gt;
&lt;li&gt;Testable: Each job is isolated and easy to test.&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>⚙️ Scalable and Ordered Queueable Execution from Triggers in Salesforce</title>
      <dc:creator>Rohit Maharashi</dc:creator>
      <pubDate>Fri, 27 Jun 2025 03:04:59 +0000</pubDate>
      <link>https://dev.to/rohit_maharashi_a25b8c6b6/scalable-and-ordered-queueable-execution-from-triggers-in-salesforce-1d44</link>
      <guid>https://dev.to/rohit_maharashi_a25b8c6b6/scalable-and-ordered-queueable-execution-from-triggers-in-salesforce-1d44</guid>
      <description>&lt;h2&gt;
  
  
  🚀 Problem Statement
&lt;/h2&gt;

&lt;p&gt;In Salesforce, Apex triggers often need to perform post-commit processing or heavy logic that exceeds synchronous limits. Queueable Apex jobs are the go-to solution for such asynchronous processing. However, there’s a critical platform limitation:&lt;/p&gt;

&lt;p&gt;When a transaction is already running asynchronously (e.g., from another Queueable/Future/Batch), only one System.enqueueJob() call is allowed.&lt;/p&gt;

&lt;p&gt;This makes it difficult to:&lt;br&gt;
    • Enqueue multiple jobs from a trigger&lt;br&gt;
    • Maintain ordered execution of those jobs&lt;br&gt;
    • Support nested internal chaining (e.g., Job C triggers C1 and C2)&lt;br&gt;
    • Handle complex input data, possibly from the trigger state&lt;br&gt;
    • Build a framework that works for any object&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;
&lt;h2&gt;
  
  
  🛠️ Design Intent
&lt;/h2&gt;

&lt;p&gt;We aim to build a generic, scalable, and governor-safe Apex framework to:&lt;br&gt;
    • Enqueue multiple Queueable jobs in order&lt;br&gt;
    • Allow jobs to chain additional sub-jobs internally&lt;br&gt;
    • Support input injection from a State object&lt;br&gt;
    • Work in both synchronous and asynchronous contexts&lt;br&gt;
    • Be reusable across object trigger handlers&lt;/p&gt;

&lt;p&gt;The final flow will look like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Trigger.afterInsert() ➔ A ➔ B ➔ C ➔ C1 ➔ C2 ➔ D ➔ E&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;⸻&lt;/p&gt;
&lt;h3&gt;
  
  
  🔧 Core Components
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;BaseChainedQueueable&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An abstract base class to support chaining jobs one after another.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public abstract class BaseChainedQueueable implements Queueable {
    protected BaseChainedQueueable nextJob;

    public void setNext(BaseChainedQueueable nextJob) {
        this.nextJob = nextJob;
    }

    public void execute(QueueableContext context) {
        run();
        if (nextJob != null) {
            System.enqueueJob(nextJob);
        }
    }

    public abstract void run();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⸻&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sample Job Classes
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JobA extends BaseChainedQueueable {
    private List&amp;lt;Account&amp;gt; accounts;

    public JobA(List&amp;lt;Account&amp;gt; accounts) {
        this.accounts = accounts;
    }

    public override void run() {
        System.debug('Running A');
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JobC extends BaseChainedQueueable {
    private List&amp;lt;Contact&amp;gt; contacts;

    public JobC(List&amp;lt;Contact&amp;gt; contacts) {
        this.contacts = contacts;
    }

    public override void run() {
        System.debug('Running C');
        JobC1 c1 = new JobC1();
        JobC2 c2 = new JobC2();
        c1.setNext(c2);

        if (nextJob != null) {
            c2.setNext(nextJob);
        }

        System.enqueueJob(c1);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JobC1 extends BaseChainedQueueable {
    public override void run() {
        System.debug('Running C1');
    }
}

public class JobC2 extends BaseChainedQueueable {
    public override void run() {
        System.debug('Running C2');
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repeat similarly for JobB, JobD, and JobE.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;State Class&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A generic key-value store to pass contextual data between jobs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class State {
    private Map&amp;lt;String, Object&amp;gt; data = new Map&amp;lt;String, Object&amp;gt;();

    public void put(String key, Object value) {
        data.put(key, value);
    }

    public Object get(String key) {
        return data.get(key);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⸻&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;QueueableOrchestrator&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Handles safe job submission based on execution context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class QueueableOrchestrator {
    public static void run(BaseChainedQueueable firstJob) {
        if (AsyncUtils.isAsync()) {
            System.enqueueJob(new ChainedStarter(firstJob));
        } else {
            System.enqueueJob(firstJob);
        }
    }

    private class ChainedStarter implements Queueable {
        private BaseChainedQueueable job;

        public ChainedStarter(BaseChainedQueueable job) {
            this.job = job;
        }

        public void execute(QueueableContext context) {
            System.enqueueJob(job);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⸻&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;JobQueueManager&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Central place to build and connect the job chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JobQueueManager {
    public static void runChainedJobsFromState(State state) {
        List&amp;lt;Account&amp;gt; accounts = (List&amp;lt;Account&amp;gt;) state.get('accounts');
        List&amp;lt;Contact&amp;gt; contacts = (List&amp;lt;Contact&amp;gt;) state.get('contacts');
        List&amp;lt;Opportunity&amp;gt; opps = (List&amp;lt;Opportunity&amp;gt;) state.get('opps');

        JobA a = new JobA(accounts);
        JobB b = new JobB(opps);
        JobC c = new JobC(contacts);
        JobD d = new JobD();
        JobE e = new JobE();

        a.setNext(b);
        b.setNext(c);
        c.setNext(d);
        d.setNext(e);

        QueueableOrchestrator.run(a);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⸻&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trigger Handler Integration
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class AccountTriggerHandler extends TriggerHandler {
    public override void afterInsert() {
        State state = new State();
        state.put('accounts', (List&amp;lt;Account&amp;gt;) Trigger.new);

        List&amp;lt;Id&amp;gt; accIds = new Map&amp;lt;Id, Account&amp;gt;(Trigger.new).keySet();
        state.put('contacts', ContactSelector.getByAccountIds(accIds));
        state.put('opps', OpportunitySelector.getByAccountIds(accIds));

        JobQueueManager.runChainedJobsFromState(state);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⸻&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Benefits Recap
&lt;/h3&gt;

&lt;p&gt;•🔁 Fully Ordered Execution — Jobs execute in defined sequence, even with sub-jobs.&lt;br&gt;
•🧱 Governor Safe — One enqueueJob() per async context.&lt;br&gt;
•🧪 Modular and Testable — Each job is self-contained and reusable.&lt;br&gt;
•🔄 Reusable Architecture — Extendable to any object’s trigger.&lt;br&gt;
•🧠 Input-Aware — Jobs receive input from a shared State.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;h3&gt;
  
  
  📚 Closing the Loop
&lt;/h3&gt;

&lt;p&gt;This pattern abstracts away common Salesforce platform limitations, providing a clean, object-oriented framework for managing chained asynchronous logic from Apex triggers.&lt;/p&gt;

&lt;p&gt;Whether you’re scaling post-processing across multiple SObjects or introducing conditional sub-flows, this solution lets you build predictably, safely, and cleanly.&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>apex</category>
      <category>development</category>
    </item>
    <item>
      <title>Enterprise-Grade Apex Trigger Architecture for Scalable Salesforce Orgs</title>
      <dc:creator>Rohit Maharashi</dc:creator>
      <pubDate>Thu, 26 Jun 2025 07:33:55 +0000</pubDate>
      <link>https://dev.to/rohit_maharashi_a25b8c6b6/enterprise-grade-apex-trigger-architecture-for-scalable-salesforce-orgs-2d0k</link>
      <guid>https://dev.to/rohit_maharashi_a25b8c6b6/enterprise-grade-apex-trigger-architecture-for-scalable-salesforce-orgs-2d0k</guid>
      <description>&lt;p&gt;As Salesforce orgs evolve into mission-critical platforms, &lt;strong&gt;Apex trigger management&lt;/strong&gt; often turns into a nightmare. Even with popular frameworks, teams eventually hit bottlenecks and complexity spirals out of control.&lt;/p&gt;

&lt;p&gt;In this article, I’ll walk you through a &lt;strong&gt;modular, maintainable, and enterprise-grade Apex trigger architecture&lt;/strong&gt; — built to scale gracefully and keep your sanity intact.&lt;/p&gt;




&lt;h2&gt;
  
  
  💥 Common Problems in Real-World Apex Trigger Implementations
&lt;/h2&gt;

&lt;p&gt;Despite best intentions, real-world triggers tend to suffer from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔁 Multiple unnecessary iterations over &lt;code&gt;Trigger.new&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;🧱 Overloaded trigger handlers that try to do too much&lt;/li&gt;
&lt;li&gt;🧵 Poor state sharing between services and layers&lt;/li&gt;
&lt;li&gt;❌ Redundant and unoptimized SOQL queries&lt;/li&gt;
&lt;li&gt;🧯 No strategy for partial failures or error aggregation&lt;/li&gt;
&lt;li&gt;🔄 Scattered DMLs that violate governor limits&lt;/li&gt;
&lt;li&gt;🔍 Difficult-to-test and debug logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need to go &lt;strong&gt;beyond&lt;/strong&gt; the usual handler frameworks and adopt &lt;strong&gt;responsibility-driven modular design&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Design Goals and Principles
&lt;/h2&gt;

&lt;p&gt;An ideal Apex trigger architecture must be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Single Responsibility&lt;/strong&gt; at every layer
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Bulk-safe&lt;/strong&gt; across all operations
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;No redundant loops&lt;/strong&gt; on &lt;code&gt;Trigger.new&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Centralized DML and error control&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Modular, reusable, and testable&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Extensible&lt;/strong&gt; for future business logic
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧩 Proposed Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Here’s the layered flow:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Trigger → Handler → Services → Selectors → UnitOfWork → ContextManager&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trigger&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lightweight. Only calls the handler.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Handler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Detects context (&lt;code&gt;beforeInsert&lt;/code&gt;, etc.) and delegates work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Services&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Business logic split into Simple (per-record) &amp;amp; Bulk (multi-record)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Selectors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encapsulate and reuse SOQL queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UnitOfWork&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Batched DML operations and error-safe commits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ContextManager&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shared state and error collection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🧪 Example: Account Trigger Flow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔹 1. Minimal Trigger
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;trigger&lt;/span&gt; &lt;span class="n"&gt;AccountTrigger&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AccountTriggerHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔹 2. Generic Trigger Handler
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;virtual&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TriggerHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;isBefore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;isInsert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;beforeInsert&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;isUpdate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;beforeUpdate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;isDelete&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;beforeDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;isAfter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;isInsert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;afterInsert&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;isUpdate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;afterUpdate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;isDelete&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;afterDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeInsert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeUpdate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeDelete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterInsert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterUpdate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterDelete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔹 3. Account-Specific Handler
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="kd"&gt;sharing&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountTriggerHandler&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;TriggerHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;AdvancedUnitOfWork&lt;/span&gt; &lt;span class="n"&gt;uow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AdvancedUnitOfWork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeInsert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AccountTriggerContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AccountTriggerContext&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;oldMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;acct&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;newRecords&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AccountValidationService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterInsert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AccountTriggerContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AccountTriggerContext&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;oldMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AccountPostInsertService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleAfterInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;newRecords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;uow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔹 4. 🧠 Domain Services
&lt;/h3&gt;

&lt;h2&gt;
  
  
  ✅ Validation Service
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="kd"&gt;sharing&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountValidationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;acct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isBlank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;acct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;Account name is mandatory.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ✅ Post-Insert Service
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="kd"&gt;sharing&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountPostInsertService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;handleAfterInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AdvancedUnitOfWork&lt;/span&gt; &lt;span class="n"&gt;uow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;relatedContacts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ContactSelector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectByAccountIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Contact&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;relatedContacts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;Updated after insert'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;uow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔹 5. 🔍 Selector Layer (SOQL Isolation)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="kd"&gt;sharing&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ContactSelector&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;selectByAccountIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;accountIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;accountIds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;accountIds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AccountId&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Contact&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;AccountId&lt;/span&gt; &lt;span class="n"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;accountIds&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔹 6. 🧱 Advanced Unit of Work
&lt;/h3&gt;

&lt;p&gt;Handles all DMLs centrally. Avoids mixed DML and supports partial error capture.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="kd"&gt;sharing&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdvancedUnitOfWork&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SObject&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inserts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SObject&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SObject&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SObject&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;registerInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SObject&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;inserts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;registerUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SObject&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;inserts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="nf"&gt;handleDml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;insert'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inserts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="nf"&gt;handleDml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;update'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;handleDml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SObject&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;SaveResult&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;insert'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;SaveResult&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isSuccess&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;TriggerContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getErrors&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;TriggerContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔹 7. 📦 Trigger Context Manager
&lt;/h3&gt;

&lt;p&gt;Shared memory and error collector across services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TriggerContextManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sharedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sharedData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;Boolean&lt;/span&gt; &lt;span class="nf"&gt;hasErrors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🧠 Why This Architecture Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;💡 One job per class = better readability and unit testing&lt;/li&gt;
&lt;li&gt;💡 Selectors prevent query duplication&lt;/li&gt;
&lt;li&gt;💡 Services remain simple and scalable&lt;/li&gt;
&lt;li&gt;💡 UnitOfWork avoids mixed DML chaos&lt;/li&gt;
&lt;li&gt;💡 ContextManager gives shared state and captures errors&lt;/li&gt;
&lt;li&gt;💡 Extremely testable: services and selectors are easy to mock/test&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ✅ Final Thoughts
&lt;/h3&gt;

&lt;p&gt;With this approach, your Salesforce trigger logic becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 Robust for enterprise-scale applications&lt;/li&gt;
&lt;li&gt;🔍 Clear and maintainable&lt;/li&gt;
&lt;li&gt;🧪 Testable by default&lt;/li&gt;
&lt;li&gt;🔄 Flexible for future changes&lt;/li&gt;
&lt;li&gt;⚙️ Aligned with governor limits and best practices&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Let your triggers do less. Let your architecture do the heavy lifting.&lt;/p&gt;

&lt;p&gt;🙌 Thanks for reading!&lt;br&gt;
If this helped you, consider sharing it with your team, leaving a ❤️, or dropping a comment!&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>apex</category>
      <category>architecture</category>
      <category>developer</category>
    </item>
  </channel>
</rss>
