Solution Architecture Best Practices
Best practices and anti-patterns for solution architecture in Salesforce CTA scenarios. These patterns emerge from real-world enterprise implementations and are the kind of practical wisdom the review board expects a CTA to demonstrate.
Design Principles
1. Declarative First, Programmatic When Necessary
Use platform capabilities before writing code. This is not dogma — it is a pragmatic principle that reduces maintenance burden and increases the pool of people who can support the solution.
When to break this principle: When declarative solutions create more complexity than code (e.g., a Flow with 50 Decision elements and 200 elements is harder to maintain than 100 lines of well-tested Apex).
2. One Automation Per Object
Consolidate all record-triggered automations into a single Flow per object per trigger context (before-save, after-save). Use subflows for logical separation.
Why: Predictable execution order, shared governor limit awareness, easier debugging, and single point of documentation.
3. Separation of Concerns
Separate business logic from UI logic from integration logic. In Apex, this means:
- Service classes for business logic (reusable across triggers, batch, API)
- Selector classes for SOQL queries (centralized, optimized)
- Domain classes for trigger logic (object-specific behavior)
- Controller classes for UI logic (LWC wire adapters, Aura controllers)
flowchart TD
subgraph UI["UI Layer"]
LWC[LWC Components]
Aura[Aura Components]
end
subgraph Controllers["Controller Layer"]
LC[Lightning Controllers]
API[REST/SOAP APIs]
end
subgraph Services["Service Layer"]
SVC[Service Classes]
end
subgraph Domain["Domain Layer"]
DOM[Domain / Trigger Handlers]
SEL[Selector Classes]
end
subgraph Platform["Platform"]
DB[(Salesforce Objects)]
end
LWC --> LC
Aura --> LC
API --> SVC
LC --> SVC
SVC --> DOM
SVC --> SEL
DOM --> DB
SEL --> DB
4. Configuration Over Customization
Use Custom Metadata Types, Custom Settings, and Custom Labels for values that change across environments or over time. Never hardcode IDs, URLs, thresholds, or business rules.
5. Design for Bulk
Every piece of automation — whether Flow or Apex — must handle bulk operations. A solution that works for one record but fails for 200 is not a solution.
6. Fail Gracefully
Every automation should have error handling. Flows need Fault paths. Apex needs try-catch blocks. Integrations need retry logic and dead-letter queues.
Architecture Patterns
Pattern: Invocable Action Bridge
Problem: Complex business logic needs to be accessible to both Flows and Apex code.
Solution: Implement the logic as an @InvocableMethod Apex class, callable from Flow and from other Apex.
When to use: When business logic is too complex for Flow but needs to be accessible to admins through Flow Builder.
Pattern: Platform Event Decoupling
Problem: Synchronous processing in a user transaction causes performance issues or governor limit pressure.
Solution: Publish a Platform Event from the user transaction, and handle the complex processing in a Platform Event-Triggered Flow or Apex trigger.
When to use: Integration callouts, complex calculations, non-critical updates that can be eventually consistent.
sequenceDiagram
participant U as User Transaction
participant PE as Platform Event Bus
participant S as Subscriber (Flow/Apex)
participant EXT as External System
U->>U: Save record
U->>PE: Publish event
U-->>U: Transaction completes (fast)
PE->>S: Deliver event (async)
S->>EXT: Callout / complex logic
EXT-->>S: Response
S->>S: Update related records
Pattern: Custom Metadata Configuration
Problem: Business rules change frequently and vary by business unit, region, or product line.
Solution: Store business rules in Custom Metadata Types. Automation reads from metadata rather than hardcoded values. Changes deploy through metadata, not code changes.
When to use: Routing rules, approval thresholds, integration endpoints, feature flags.
Pattern: Apex Enterprise Patterns
Problem: Large-scale Apex development becomes unmaintainable without structure.
Solution: Adopt the Apex Enterprise Patterns (fflib):
- Unit of Work for transactional DML
- Selector Layer for SOQL
- Domain Layer for trigger logic
- Service Layer for business logic
When to use: Enterprise-scale orgs with dedicated development teams and complex business logic.
CTA Signal
Recommending Apex Enterprise Patterns shows the review board that you think about long-term maintainability, not just solving today’s problem. But only recommend it when the org’s complexity warrants it — it is over-engineering for a simple org.
Anti-Patterns
Anti-Pattern: The God Flow
What it looks like: A single Flow with 500+ elements that handles every possible scenario for an object.
Why it is bad: Impossible to debug, test, or modify without risk of breaking unrelated logic. Approaches the 250K element limit quickly.
Fix: Break into a master Flow with subflows. Each subflow handles one logical concern.
Anti-Pattern: Apex Everywhere
What it looks like: Every requirement is implemented in Apex, even simple field updates and validations.
Why it is bad: Creates developer dependency for all changes. Increases deployment complexity. Ignores platform capabilities.
Fix: Evaluate each requirement against the decision guides. Use Apex only when the decision framework clearly points to it.
Anti-Pattern: AppExchange Without Evaluation
What it looks like: Installing AppExchange packages without a formal evaluation, TCO analysis, or exit strategy.
Why it is bad: Creates vendor lock-in, unknown governor limit impact, data model pollution, and upgrade risk.
Fix: Use the vendor evaluation scorecard for every AppExchange decision.
Anti-Pattern: Hardcoded Everything
What it looks like: Record Type IDs, profile IDs, queue IDs, and endpoint URLs hardcoded in Apex or Flow.
Why it is bad: Breaks during deployment to different environments. Creates hidden dependencies.
Fix: Use Custom Metadata Types for configurable values. Use Schema.SObjectType describes for IDs. Use Named Credentials for endpoints.
Anti-Pattern: No Error Handling
What it looks like: Flows without Fault paths. Apex without try-catch. Integrations without retry logic.
Why it is bad: Users see cryptic error messages. Data ends up in inconsistent states. Integration failures go unnoticed.
Fix: Every Flow gets a Fault path that logs errors and shows user-friendly messages. Every Apex class gets try-catch with structured error logging. Every integration gets retry logic with alerting.
Anti-Pattern: Legacy Automation Soup
What it looks like: A mix of Workflow Rules, Process Builder, and Flow on the same objects. Some objects have all three plus Apex triggers.
Why it is bad: Unpredictable execution order. Governor limit overrun. Impossible to debug.
Fix: Consolidate all automation to Flow + Apex triggers. Workflow Rules and Process Builder are deprecated — migrate systematically.
Checklist: Solution Architecture Review
Before finalizing a solution architecture in a CTA scenario, verify:
- Every automation has a clear owner (admin vs developer)
- Governor limits have been estimated for peak transaction scenarios
- Error handling exists for every automation and integration
- No hardcoded IDs, URLs, or business rules
- One automation per object per trigger context
- AppExchange packages have been evaluated with the vendor scorecard
- Exit strategies exist for all third-party dependencies
- The solution accounts for bulk data operations (data loader, integration bulk loads)
- Testing strategy covers both declarative and programmatic components
- Performance has been considered for the largest transaction scenarios
Related Topics
- Declarative vs Programmatic — Flow vs Apex decision framework
- Build vs Buy — AppExchange evaluation and TCO analysis
- Decision Guides — Visual decision flowcharts
- Trade-Offs — Detailed trade-off analysis
- Governance Model — How governance supports these best practices
Sources
- Salesforce Architects: Well-Architected Framework
- Salesforce Developer Documentation: Apex Enterprise Patterns
- Andrew Fawcett: Enterprise Architecture Patterns on Force.com
- Salesforce Architects: Automation Best Practices
- CTA Study Groups: Community-sourced anti-patterns from CTA exam preparation