5. Design Pattern Catalogue
We organize 14 patterns into three categories based on the primary constraint they address. Each pattern is documented following an abbreviated pattern template: Context, Problem, Forces, Solution, Consequences, and Known Uses.
5.1. Governor-Aware Patterns
Governor-Aware Patterns optimize resource consumption within platform-enforced execution limits. These patterns have no direct equivalent in classical design pattern literature because they respond to constraints that do not exist in single-tenant environments.
Pattern 1: Bulkification
Context: Business logic must process records that arrive in variable batch sizes — from a single record via UI interaction to 200 records via API bulk operations or data loads.
Problem: Code written to handle a single record at a time will exceed governor limits when processing batches, causing runtime failures in production.
Forces:
Platform governor limits are per-transaction, not per-record
API and bulk operations deliver up to 200 records per trigger invocation
Developers naturally reason about single-record logic
Solution: Design all data access and manipulation logic to operate on collections (lists, sets, maps) rather than individual records. Move all SOQL queries and DML operations outside of loops. Use map-based lookups to replace repeated queries.
Figure 3.
Bulkification pattern: replacing per-record operations with collection-based processing.
Figure 3.
Bulkification pattern: replacing per-record operations with collection-based processing.
Consequences:
(+) Eliminates governor limit exceptions under bulk data operations
(+) Consistent performance regardless of batch size (1 to 200 records)
(-) Increases code complexity; developers must think in terms of collections
(-) Requires more upfront design compared to iterative single-record logic
Known Uses: Every production Salesforce implementation observed by the author employs this pattern. At a Tier-1 US telecommunications provider (Telco-A), where the org contained 400 million records on day one, bulkification was a mandatory code review criterion. At another major wireless carrier (Telco-B), refactoring Flows to use bulk processing patterns increased daily throughput from 500,000 to 4 million records (an 8x improvement).
Pattern 2: Queueable Chain
Context: A business process requires more computation than a single synchronous transaction allows (10,000ms CPU time, 100 SOQL queries) but must complete as a coordinated sequence.
Problem: Long-running processes that exceed single-transaction governor limits fail mid-execution, leaving data in an inconsistent state.
Forces:
Synchronous transactions have strict CPU and query limits
Asynchronous contexts (Queueable, Batch) have higher limits but cannot be directly chained in unlimited depth
Business processes require sequential ordering of steps
Error handling must allow resumption from the point of failure
Solution: Decompose the process into discrete units of work, each implemented as a Queueable Apex class. Each unit, upon successful completion, enqueues the next unit in the chain. State is passed between units via serialized parameters or a custom orchestration object.
Figure 4.
Queueable Chain pattern with orchestration tracking.
Figure 4.
Queueable Chain pattern with orchestration tracking.
Consequences:
(+) Enables processes that exceed single-transaction limits
(+) Provides natural checkpointing for error recovery
(+) Each step runs in its own governor context with fresh limits
(-) Introduces asynchronous complexity; harder to debug than synchronous code
(-) Platform limits on chain depth (currently 1 Queueable per Queueable in some contexts)
(-) Requires orchestration infrastructure for monitoring and retry
Known Uses: At a Fortune 500 consumer goods manufacturer (CPG-A), a Hyper Batch framework was designed using this pattern to process complex scheduling computations for 25,000 frontline employees. At a global data and analytics provider (DataCo-A), CPQ pricing calculations that exceeded synchronous limits were decomposed into chained Queueable steps.
Pattern 3: Selective SOQL
Context: Business logic requires querying related data, but query complexity and result set size vary dramatically based on runtime conditions.
Problem: Queries that retrieve all fields and all related records consume SOQL query rows (50,000 limit) and heap memory, even when only a subset is needed.
Solution: Build queries dynamically based on actual data requirements using the Selector Layer pattern [
17] to centralize query construction with field-level, relationship-level, and row-level filtering based on calling context.
Consequences: (+) Minimizes governor consumption and heap usage per transaction. (-) Dynamic query construction requires security discipline to prevent SOQL injection.
Known Uses: The fflib Selector Layer [
17] operationalizes this pattern. At Auto-A, consolidating 14 business units required Selective SOQL to manage dramatically different data volumes across business unit record types.
Pattern 4: Lazy Initialization
Context: An object or data set is expensive to construct but may not be needed in every code path within a transaction.
Problem: Eagerly loading all potentially needed data wastes governor resources when execution paths do not require it.
Solution: Defer data loading until first access using private properties with null-check accessors that initialize on first invocation and cache results within the transaction.
Consequences: (+) Reduces unnecessary governor consumption; particularly effective in trigger handlers managing multiple event types. (-) Can make execution order less predictable; cached values are not persisted across transactions.
Known Uses: Standard practice in enterprise Apex codebases. At Pharma-A, Lazy Initialization reduced average SOQL consumption by deferring queries to only the integration paths activated by each transaction across 13+ integration touchpoints.
Pattern 5: Governor Limit Monitor
Context: Complex business logic approaches governor limits during execution, and the consequences of exceeding limits (transaction abort) are unacceptable for business-critical processes.
Problem: Without runtime awareness of governor consumption, code cannot adapt its behavior when approaching limits, resulting in hard failures.
Forces:
Platform provides runtime introspection of governor consumption (Limits class)
Some processes can degrade gracefully (defer remaining work) rather than abort entirely
Monitoring overhead must be minimal to avoid consuming the resources being monitored
Solution: Instrument business logic with checkpoints that query remaining governor limits (using the platform's Limits class). When consumption approaches thresholds (e.g., 80% of SOQL queries consumed), switch to a degraded mode: reduce query scope, defer non-critical operations to asynchronous processing, or log a warning and enqueue remaining work via the Queueable Chain pattern.
Figure 5.
Governor Limit Monitor pattern with graceful degradation.
Figure 5.
Governor Limit Monitor pattern with graceful degradation.
Consequences:
(+) Prevents hard transaction failures in complex, variable-load scenarios
(+) Enables graceful degradation rather than all-or-nothing execution
(-) Adds monitoring overhead to every checkpoint
(-) Requires defining "degraded mode" behavior for each process, increasing design complexity
Known Uses: At a major US wireless carrier (Telco-B), the integration pipeline processing 4 million records daily uses governor monitoring to dynamically adjust batch sizes. At a Fortune 500 consumer goods manufacturer (CPG-A), the Hyper Batch framework employed this pattern to ensure scheduling calculations completed even under variable data loads.
5.2. Multi-Tenant Isolation Patterns
Multi-Tenant Isolation Patterns ensure data and process separation in shared infrastructure environments. These patterns address the unique security, compliance, and operational challenges of multi-tenant architecture.
Pattern 6: Tenant-Scoped Configuration
Context: An application must behave differently across business units, regions, or customer segments within a single multi-tenant CRM organization.
Problem: Hard-coding configuration values creates deployment and maintenance overhead. Environment-specific configurations cannot be managed through code deployments in production environments.
Forces:
CRM platforms prohibit direct database manipulation in production
Configuration must be changeable by administrators without developer involvement
Different business units within the same org may require different thresholds, routing rules, or feature toggles
The Twelve-Factor App methodology [
28] prescribes strict separation of config from code
Solution: Use the platform's metadata-driven configuration mechanisms — Custom Metadata Types, Custom Settings, or Custom Labels — to externalize all environment-specific and tenant-specific configuration. Organize configurations hierarchically (org-wide defaults overridden by profile or user-level settings).
Figure 6.
Tenant-Scoped Configuration pattern with hierarchical overrides.
Figure 6.
Tenant-Scoped Configuration pattern with hierarchical overrides.
Consequences:
(+) Eliminates hard-coded values; enables admin-driven configuration changes
(+) Custom Metadata Types are deployable through CI/CD pipelines
(+) Supports multi-business-unit architectures within a single org
(-) Over-reliance on configuration can create a "configuration sprawl" anti-pattern
(-) Custom Settings cached in memory count against governor limits
Known Uses: At Auto-A, consolidating 14 business units required extensive Tenant-Scoped Configuration for unit-specific approval thresholds, routing rules, and feature toggles. At TechCo-A, configuration-driven workflows enabled different approval hierarchies per business function.
Pattern 7: Namespace Partitioning
Context: Multiple development teams or managed packages contribute code and metadata to the same CRM organization.
Problem: Without naming conventions, metadata conflicts arise between teams, packages, and org-level customizations, leading to deployment failures and runtime errors.
Solution: Establish hierarchical naming conventions prefixing all metadata with team or domain identifiers. Use unlocked packages (Salesforce DX) [
26] to create formal dependency boundaries between teams without the overhead of managed packages.
Consequences: (+) Prevents metadata collisions; enables independent development and deployment cycles; unlocked packages enforce dependency graphs. (-) Naming conventions require governance tooling; cross-package references create coupling.
Known Uses: At CPG-A, managing 50+ developers required strict namespace partitioning to prevent deployment conflicts.
Pattern 8: Security Boundary Enforcement
Context: Enterprise CRM implementations must enforce data visibility rules that align with organizational hierarchy, regulatory requirements, and partnership structures.
Problem: CRM platforms provide multiple overlapping security mechanisms (OWD, Role Hierarchy, Sharing Rules, Apex Sharing, Restriction Rules), and incorrect configuration creates data leakage or over-restriction.
Forces:
Regulatory requirements (HIPAA, GDPR, SOX) demand provable data isolation
Performance degrades with complex sharing calculations on large data volumes
Security models must accommodate both hierarchical (role-based) and lateral (team-based) access patterns
Point-in-time security requirements (financial services) conflict with platform-native sharing models
Solution: Design security from the data model outward. Start with the most restrictive Organization-Wide Defaults (Private), then layer role hierarchy, sharing rules, and programmatic sharing to open access precisely where needed. Use Restriction Rules for hard compliance boundaries that cannot be overridden. For point-in-time requirements, implement Apex-managed sharing with explicit share record management.
Figure 7.
Security Boundary Enforcement: layered access control from restrictive baseline.
Figure 7.
Security Boundary Enforcement: layered access control from restrictive baseline.
Consequences:
(+) Provable data isolation for regulatory compliance
(+) Layered approach simplifies auditing
(-) Complex sharing models degrade query performance at scale
(-) Apex Managed Sharing requires careful maintenance as organizational structure evolves
Known Uses: At FinServ-A, Financial Service Cloud required point-in-time security where relationship managers could only see client data during their active advisory period, implemented through Apex-based temporal sharing. At Pharma-A, HIPAA compliance required Restriction Rules to prevent cross-division data access.
Pattern 9: Cross-Org Data Federation
Context: An enterprise operates multiple CRM organizations (due to mergers, acquisitions, or regulatory requirements) that need to share selected data without full consolidation.
Problem: Full org consolidation is prohibitively expensive and disruptive for organizations with established processes, integrations, and customizations.
Solution: Implement federation through Salesforce Connect (OData protocol) to expose external org data as External Objects, or use middleware-mediated synchronization for selected data sets. Maintain a Master Data Management (MDM) strategy designating data ownership per entity per org.
Consequences: (+) Avoids consolidation cost and disruption; respects data residency requirements; allows gradual convergence. (-) External Objects have query limitations; federated queries introduce latency; MDM governance overhead is significant.
Known Uses: At Telco-A, Salesforce Connect enabled real-time access to legacy system data without migration — the org had 400 million records on day one. At Auto-A, the architecture assessed federation vs. consolidation for 14 business units before determining consolidation was feasible.
5.3. Platform Evolution Patterns
Platform Evolution Patterns enable applications to adapt to platform releases, API version changes, and evolving capabilities without regression.
Pattern 10: Metadata-Driven Configuration
Context: Business rules and process behaviors change frequently but code deployments are governed by release management processes with lead times.
Problem: Embedding business rules in code requires developer involvement and deployment cycles for changes that are business-operational in nature.
Forces:
Business agility demands rapid rule changes (pricing rules, routing logic, SLA thresholds)
Code deployments require testing, staging, and change management
CRM platforms provide metadata mechanisms that administrators can modify in production
Not all business logic can be expressed declaratively
Solution: Decompose business logic into a stable code framework that reads its behavioral parameters from platform metadata (Custom Metadata Types, Flow definitions, or custom configuration objects). The code framework remains static across releases; behavior changes are effected through metadata changes managed by business administrators.
Figure 8.
Metadata-Driven Configuration: separating stable code from dynamic business rules.
Figure 8.
Metadata-Driven Configuration: separating stable code from dynamic business rules.
Consequences:
(+) Business rule changes do not require code deployments
(+) Reduces developer bottleneck for operational configuration changes
(+) Custom Metadata Types are versionable and deployable through CI/CD when needed
(-) Requires upfront investment in building the metadata-reading framework
(-) Complex rule interactions may exceed what metadata can express, requiring code changes
Known Uses: At TechCo-A, approval hierarchies and routing rules were metadata-driven, enabling business teams to modify procurement workflows without developers. At CPG-A, scheduling rules for 25,000 field workers were externalized to metadata for regional manager adjustment.
Pattern 11: Feature Toggle
Context: New features must be deployed to production but activated selectively — by user group, business unit, or rollout percentage.
Problem: Deploying features to all users simultaneously creates risk, and rolling back CRM deployments is complex and error-prone.
Solution: Implement feature toggles using Custom Permissions (user/profile-level) or Custom Metadata Types (org-level). Guard new feature code paths with toggle checks that can be activated or deactivated without code changes.
Consequences: (+) Enables phased rollouts and rapid feature deactivation. (-) Accumulated toggles create "toggle debt"; testing combinatorial states increases QA complexity.
Known Uses: At Auto-A, feature toggles governed the phased rollout of CPQ capabilities to 14 business units over 18 months.
Pattern 12: Backward-Compatible Polymorphism
Context: A CRM application must support multiple versions of an API, business process, or integration protocol simultaneously during migration periods.
Problem: Breaking changes disrupt downstream consumers, but indefinite backward compatibility creates unsustainable maintenance burden.
Solution: Define stable interfaces (Apex interfaces or abstract classes) that represent the contract. Implement version-specific behavior in concrete classes selected at runtime through a factory based on caller context or configuration. Deprecate old implementations on a published schedule.
Figure 9.
Backward-Compatible Polymorphism using interface-based version management.
Figure 9.
Backward-Compatible Polymorphism using interface-based version management.
Consequences:
(+) Enables non-disruptive API evolution
(+) Consumers migrate on their own timeline
(-) Maintaining multiple implementations increases codebase size
(-) Factory logic must be managed and tested for all active versions
Known Uses: At a global data and analytics provider (DataCo-A), migrating from a legacy CPQ platform to Salesforce CPQ required maintaining dual processing paths for 12+ months while downstream systems migrated.
Pattern 13: Event-Driven Decoupling
Context: Multiple systems (CRM, ERP, data warehouse, marketing automation) must stay synchronized without creating brittle point-to-point integrations.
Problem: Direct system-to-system integrations create tight coupling where changes to one system cascade failures to all connected systems.
Forces:
Enterprise landscapes comprise 10-50+ integrated systems
Each system has different availability SLAs and maintenance windows
Data consistency requirements vary (eventual vs. strong consistency)
Integration patterns have evolved from batch ETL to near-real-time event streams [
7,
22]
Solution: Use the CRM platform's event bus (Platform Events, Change Data Capture) as the integration backbone. Publishers emit domain events without knowledge of subscribers. Subscribers process events asynchronously, with the platform managing delivery guarantees and replay capabilities. Middleware (MuleSoft, Boomi) acts as an event mediator for systems that cannot directly consume platform events.
Figure 10.
Event-Driven Decoupling using Platform Events and Change Data Capture.
Figure 10.
Event-Driven Decoupling using Platform Events and Change Data Capture.
Consequences:
(+) Eliminates brittle point-to-point integrations
(+) Publishers and subscribers evolve independently
(+) Platform-managed event delivery with replay capability
(-) Eventual consistency requires business process adaptation
(-) Event schema evolution must be managed carefully [
23]
(-) Debugging asynchronous event chains is more complex than synchronous calls
Known Uses: At Telco-A, 22 integrations were delivered in 10 weeks using MuleSoft as event mediator. At Telco-B, event-driven middleware handles 4 million daily records with retry and dead letter queues. At Pharma-A, 13+ integrations were managed through event-mediated architecture using Informatica and Boomi.
Pattern 14: Layered Test Architecture
Context: Enterprise CRM applications require comprehensive testing but platform constraints (governor limits, shared environment, metadata dependencies) make testing fundamentally different from traditional application testing.
Problem: Test methods in multi-tenant CRM platforms share governor limits with the code under test, meaning test setup and assertions consume the same finite resources as the business logic being validated.
Forces:
Platform requires minimum 75% code coverage for production deployment
Test methods run in an isolated transaction (seeAllData=false by default)
Test data creation consumes DML and SOQL limits
Integration tests that call external systems are prohibited in test context
Complex org metadata (validation rules, flows, triggers) fire during test execution
Solution: Organize tests in three layers: (1) Unit Tests that validate individual methods using dependency injection and mock objects (HttpCalloutMock, StubProvider); (2) Integration Tests that validate cross-object business processes using test data factories; and (3) End-to-End Tests executed outside the platform (Selenium, Provar) for UI-level validation.
Figure 11.
Layered Test Architecture: test pyramid adapted for multi-tenant CRM platforms.
Figure 11.
Layered Test Architecture: test pyramid adapted for multi-tenant CRM platforms.
Consequences: (+) Maximizes test coverage while minimizing governor consumption; mock-based unit tests execute quickly without org data dependency. (-) Requires investment in test data factories and mock frameworks; end-to-end tests are fragile and expensive to maintain.
Known Uses: At DataCo-A, Copado was used as the CI/CD platform with layered test execution. At CPG-A, the CoE established test architecture standards for all 50+ developers. The author's Udemy course [
26] teaches CI/CD test pipeline architecture.