AgentSkillsCN

Cross-Cutting Platform Patterns

贯穿多项Salesforce技术的共通概念——执行顺序、共享模式、安全机制、治理理念。

SKILL.md
--- frontmatter
name: Cross-Cutting Platform Patterns
description: Shared concepts that span multiple Salesforce technologies — order of execution, sharing model, security, governor philosophy
when_to_use: When debugging save-order issues, understanding sharing/security behavior, or deciding between platform features
version: "62.0"

Cross-Cutting Platform Patterns

Salesforce Order of Execution (Record Save)

The full sequence when a record is saved. Every Apex and Flow developer needs this memorized.

StepWhat Happens
1Original record loaded from DB (or initialized for insert)
2New field values overwrite old values (from request)
3System validation rules (required fields, field formats)
4Before-save Flows (record-triggered, fast field updates)
5Before triggers execute
6System validation again + custom validation rules
7Duplicate rules execute
8Record saved to DB (but not committed)
9After triggers execute
10Assignment rules, auto-response rules, workflow rules
11Workflow field updates — if any fire, before/after triggers re-fire
12After-save Flows (record-triggered, can do DML)
13Entitlement rules, roll-up summary fields
14Cross-object formula updates, repeat validation if needed
15Criteria-based sharing evaluation
16DML committed to database

Key implications:

  • Before-save Flows (step 4) run before before triggers (step 5)
  • Validation rules run twice — once before triggers, once after
  • Workflow field updates (step 11) can re-fire triggers, causing unexpected re-entry
  • After-save Flows (step 12) run after triggers but before commit — they share the transaction
  • Nothing is committed until step 16. Any unhandled exception rolls back everything

Governor Limits Philosophy

Salesforce is multi-tenant. Governor limits exist to prevent any single transaction from monopolizing shared resources.

Key principles:

  • Limits are per transaction, not per trigger or class
  • A trigger that calls a helper that calls a service — all share the same limits
  • Async contexts (Batch, Queueable, @future) get fresh limits per execution
  • Limits class methods let you check consumption at runtime

The multi-tenant mental model: Your org shares compute, storage, and I/O with thousands of other orgs on the same pod. Limits are the contract that keeps everyone honest.

Sharing Model Overview

Salesforce record access is deny by default — users see nothing unless access is explicitly granted.

Access Grant Hierarchy (evaluated in order)

LayerScopeAdmin Control
Org-Wide Defaults (OWD)Baseline for all records per objectSetup → Sharing Settings
Role HierarchyUsers see records owned by subordinatesSetup → Roles
Sharing RulesCriteria-based or owner-based bulk grantsSetup → Sharing Settings
Manual SharingRecord-by-record grants by ownerRecord detail → Sharing button
Apex Managed SharingProgrammatic grants via Share objectsCode: AccountShare, etc.
Implicit SharingParent-child auto-grants (Account→Contact)Automatic, not configurable
Territory ManagementGeographic/account-based accessSetup → Territory Management

OWD Settings

SettingMeaning
PrivateOnly owner + users above in role hierarchy
Public Read OnlyAll users can read, only owner can edit
Public Read/WriteAll users can read and edit
Controlled by ParentAccess inherited from parent (e.g., Contact → Account)
Public Full AccessRead, edit, transfer, delete (Campaign only)

Apex Sharing Keywords

KeywordBehavior
with sharingEnforces current user's sharing rules
without sharingIgnores sharing rules (runs as system)
inherited sharingUses sharing context of the calling class
(no keyword)Same as without sharing in most contexts — always specify explicitly

Rule of thumb: Use with sharing by default. Use without sharing only when the business logic requires seeing all records (e.g., a rollup service). Use inherited sharing for utility classes that shouldn't make their own sharing decision.

Security Model

Profile vs Permission Set

AspectProfilePermission Set
AssignmentOne per user (required)Many per user (additive)
DirectionMoving toward minimal profilesPreferred for all grants
Best practiceUse "Minimum Access" profileGrant everything via perm sets

CRUD and FLS

LevelWhat It ControlsWhere Set
Object CRUDCreate, Read, Update, Delete per objectProfile or Permission Set
Field-Level Security (FLS)Read/Edit per field per objectProfile or Permission Set

Enforcement in Apex:

apex
// Option 1: SOQL-level enforcement (Spring '20+)
List<Account> accts = [SELECT Name FROM Account WITH SECURITY_ENFORCED];

// Option 2: Strip inaccessible fields from results
SObjectAccessDecision decision = Security.stripInaccessible(
    AccessType.READABLE,
    [SELECT Name, AnnualRevenue FROM Account]
);
List<Account> safe = decision.getRecords();

// Option 3: Manual check (oldest approach)
if (Schema.sObjectType.Account.fields.Name.isAccessible()) {
    // safe to read
}
MethodThrows on ViolationStrips SilentlyWorks in SOQL
WITH SECURITY_ENFORCEDYes (insufficient access)NoYes
stripInaccessible()NoYesNo (post-query)
Schema.describeNo (manual check)NoNo

Platform Events

Publish/subscribe messaging on the Salesforce platform.

When to Use Platform Events

Use CasePlatform EventsOther Options
Cross-system integrationYesExternal services, MuleSoft
Decoupled Apex-to-ApexYesCustom notifications, Queueable
Trigger-to-LWC real-timeYes (via empApi)Streaming API
Logging/audit trailYesCustom objects, Big Objects
Error notificationYesCustom notifications, email

Key Characteristics

  • Fire-and-forget — publisher doesn't wait for subscribers
  • Survive transaction rollback — published events persist even if the publishing transaction fails (when using EventBus.publish after setSavepoint/rollback)
  • Replay — subscribers can replay events from the last 72 hours (or 3 days)
  • Governor limits — publishing counts against DML limits; subscribing via trigger counts against trigger limits
  • AFTER_COMMIT vs AFTER_EACH — controls when the trigger fires relative to the batch
apex
// Publishing
MyEvent__e evt = new MyEvent__e(Payload__c = 'data');
Database.SaveResult sr = EventBus.publish(evt);

// Subscribing (Apex trigger)
trigger MyEventTrigger on MyEvent__e (after insert) {
    for (MyEvent__e evt : Trigger.new) {
        // process event
    }
}

Custom Metadata Types vs Custom Settings

AspectCustom Metadata TypesCustom Settings (List)Custom Settings (Hierarchy)
DeployableYes (metadata API)No (data)No (data)
PackageableYesPartiallyPartially
SOQL to queryYes (no limit count)Yes (counts against limit)No — use getInstance()
CachedYesYes (list)Yes (hierarchy)
Per-user valuesNoNoYes (profile/user level)
Editable in ApexNo (read-only)YesYes
Use whenConfig that deploys with codeApp-level settings, mutableUser/profile-specific settings

Modern guidance: Prefer Custom Metadata Types for anything that should travel with your metadata deployments. Use Custom Settings only when you need mutability in Apex or per-user/profile configuration.