Skip to main content

Setup vs Non-Setup Objects in Salesforce: Architecture, Differences & Mixed DML Guide

In plain words: Setup objects control who can log in and what they are allowed to see (security, users, groups, and permissions). Non-setup objects hold the actual business records your users work on daily (accounts, contacts, opportunities, and custom tables). Mixing DML operations on both in the same transaction causes Salesforce to stop and throw a Mixed DML error.

In Salesforce architecture, every sObject belongs to one of two foundational categories: Setup Objects or Non-Setup Objects. Distinguishing between them is essential not only for security governance and org configuration, but also for avoiding transactional conflicts in Apex development.

1. What Are Setup Objects?

Setup objects (often called metadata configuration objects) govern system security, administrative permissions, user provisioning, and org-level behavior. They define the rules of engagement across your platform.

  • User & UserRole: Individual user accounts, role hierarchy definitions, and profile assignments.
  • PermissionSet & PermissionSetAssignment: Fine-grained object, field, and system capability grants assigned directly to users.
  • Group & GroupMember: Public groups and queues used for automated record assignment and sharing rules.
  • QueueSobject: Defines which sObjects (e.g., Lead, Case, Custom Objects) are supported by specific Queues.
  • Territory Management Objects: UserTerritory2Association and territory assignment models.

2. What Are Non-Setup Objects?

Non-setup objects represent real-world customer interactions and core business transactional data. Every standard customer-facing object and user-defined custom object falls under this category.

  • Standard CRM Entities: Account, Contact, Lead, Opportunity, Case, Order, and Contract.
  • Custom Business Objects: Any custom table ending in __c (e.g., Invoice__c, Project__c, Expense__c).
  • Task & Event Activities: Standard activity records linked directly to customer objects.
360 Setup vs Non-Setup Architecture Card:
  • Setup Purpose: Configures permissions, visibility, users, and security boundaries.
  • Non-Setup Purpose: Stores transactional operational data and customer relationship records.
  • Database Restriction: Synchronous DML cannot modify both categories within the same transaction context.
  • Transaction Error: Violating this boundary triggers System.MixedDmlException: DML operation on setup object not permitted after you have updated a non-setup object.

3. The Mixed DML Conflict Explained

Salesforce blocks developers from modifying a Setup object and a Non-Setup object in the same transaction to prevent permission and sharing recalculation corruption. Because altering a user's role, public group, or permission set changes record visibility across the entire platform, Salesforce must commit those security alterations in an isolated transaction before allowing record inserts.

Developer Trap: The Synchronous Mixed DML Failure
Executing an insert or update on an Account followed immediately by an insert on a UserRole or PermissionSetAssignment within the same synchronous execution path causes an immediate, fatal transaction rollback.

4. How to Resolve Mixed DML Exceptions in Apex

To perform operations on both object types, you must split the transaction boundary using either asynchronous processing in production code or System.runAs() in test classes.

Solution A: Production Code via Asynchronous Apex (@future / Queueable)
Inserting an Account synchronously and delegating the Setup Object assignment to an asynchronous thread:
public with sharing class UserOnboardingService {
    public static void onboardNewHire(String accountName, Id targetUserId, Id permissionSetId) {
        // 1. DML on Non-Setup Object (Account)
        Account acc = new Account(Name = accountName);
        insert as user acc;

        // 2. Offload Setup Object DML to an asynchronous thread
        assignPermissionSetAsync(targetUserId, permissionSetId);
    }

    @future
    public static void assignPermissionSetAsync(Id userId, Id permSetId) {
        PermissionSetAssignment psa = new PermissionSetAssignment(
            AssigneeId = userId,
            PermissionSetId = permSetId
        );
        insert psa;
    }
}
Solution B: Unit Tests via System.runAs()
In test classes, separating setup and non-setup DML using isolated user context blocks:
@isTest
private class UserOnboardingTest {
    @isTest
    static void testMixedDmlResolution() {
        User adminUser = [SELECT Id FROM User WHERE Profile.Name = 'System Administrator' AND IsActive = true LIMIT 1];

        System.runAs(adminUser) {
            // 1. Setup Object DML inside runAs block
            UserRole r = new UserRole(DeveloperName = 'CustomSalesRole', Name = 'Sales Rep');
            insert r;
        }

        // 2. Non-Setup Object DML in main test execution
        Test.startTest();
        Account testAcc = new Account(Name = 'Acme Test Corp');
        insert testAcc;
        Test.stopTest();

        Assert.isNotNull(testAcc.Id, 'Account should be inserted successfully without Mixed DML errors.');
    }
}

5. Core Best Practices & Rules

Core Rule: Never execute synchronous DML on Setup and Non-Setup objects in the same execution context. Always separate them using @future or Queueable Apex in production, and System.runAs() in test methods.
  • Use Queueable Apex for Complex Chaining: When passing complex sObject parameters rather than primitive IDs, use Queueable Apex instead of @future to handle setup modifications.
  • Design Trigger Handlers Defensively: Ensure user creation triggers do not invoke synchronous helper methods that create default business records (Account, Task) without asynchronous delegation.
  • Enforce User Mode Security: Always execute queries and DML with WITH USER_MODE and as user to maintain compliance with Object-Level and Field-Level permissions.

Summary

Understanding the clear boundary between Setup and Non-Setup objects is essential for building scalable, error-free Salesforce architectures. By keeping administrative security records separate from business data transactions and decoupling them with asynchronous Apex patterns, you eliminate MixedDmlException errors and ensure seamless operations across your org.