Data Virtualization Patterns
Salesforce Connect lets you expose data that lives outside Salesforce
as External Objects — sObject-like records that look native in
the UI, in SOQL, and in Apex, but whose rows are fetched on demand
from a remote source. The data never lands in Salesforce storage.
The promise is appealing: no ETL, no replication lag, no extra
storage cost, single source of truth. The reality has sharp edges.
External Objects are not full sObjects. Many capabilities customers
expect from a custom object — triggers, validation rules,
record-triggered flows, formula fields referencing other records,
roll-up summaries, full-text search, audit trails — are absent or
significantly restricted. Practitioners who skip the limits review
discover the gaps in production.
This skill is a decision and configuration guide. It helps you pick
virtualize vs replicate for a specific data set, then walks you
through the External Object configuration that avoids the most
common production problems.
When virtualization is the right answer
Virtualization is appropriate when all of the following hold:
- The data is read-mostly from Salesforce's perspective. Writes back
to the source are possible (OData 4.0 with writable adapter), but
introduce more failure modes.
- The dataset is large enough that replicating it is expensive in
storage cost or sync complexity, and the remote source is the
authoritative system of record.
- Salesforce automation requirements are limited to display and
cross-object reference (a Contact -> ExternalAccount lookup), not
triggers, validation, or record-triggered flows on the external
rows.
- The remote source can serve a request within the page-load budget
(a few hundred milliseconds) for the typical row counts a Lightning
page or list view will request.
When the workload is write-heavy, automation-heavy, or latency-
sensitive, replication into a regular custom object is the right
call — even with the storage and freshness tradeoffs.
Adapter choices
Salesforce Connect ships several adapters; pick by the source's
protocol and the cross-org pattern needed.
| Adapter |
Use when |
| OData 2.0 |
Source exposes an OData 2.0 endpoint; legacy partners |
| OData 4.0 |
Source exposes OData 4.0; preferred for new builds; supports writes |
| Cross-Org |
Source is another Salesforce org; uses Salesforce-to-Salesforce protocol |
| Custom (Apex) |
Source is REST / GraphQL / non-OData; implement DataSource.Provider and DataSource.Connection |
The Custom (Apex) adapter is the escape hatch but carries the
ownership cost of writing the connector code, handling pagination,
mapping types, and dealing with auth refresh.
Recommended Workflow
- Confirm the use case is read-mostly and automation-light. Validate that External Object's "no triggers, no record-triggered flows, no validation rules, no roll-up summary" limits do not block requirements. If they do, replicate instead.
- Pick the adapter. OData 4.0 if the source can speak it; cross-org for org-to-org; custom Apex for everything else. Avoid OData 2.0 for new builds unless the legacy partner blocks an upgrade.
- Define indirect lookup keys. External Objects do not have native AccountId joins; you use Indirect Lookups that join on an External Id field. Confirm the External Id is unique and indexed on the Salesforce-side parent.
- Size the callout budget. Each list view, related list, or page render that touches an External Object issues a callout. The per-transaction callout cap (100 sync) and per-24-hour external-object callout caps matter at scale; do not assume the limits are unlimited.
- Test the negative paths. Source down, source slow, source returns malformed data, auth token expired. The platform's behavior on each differs — slow source produces page-load timeouts, down source produces blank related lists, malformed data fails silently.
- Document the practitioner contract. Make explicit in admin / dev documentation that this object cannot have triggers, validation, or record-triggered flows. Without this, the next admin will try to add one and be confused when it is not in the picker.
What This Skill Does Not Cover
| Topic |
See instead |
| Plain REST callouts (no External Object) |
integration/named-credential-patterns |
| One-time ETL / data migration |
data/data-migration-strategy |
| Big Objects (append-only, async query) |
data/big-objects-patterns |
| Change Data Capture out of Salesforce |
integration/change-data-capture-patterns |
1---2name: data-virtualization-patterns3description: Choosing between virtualizing external data into Salesforce (External Objects via Salesforce Connect / OData 2.0 / 4.0 / cross-org adapter) and replicating it into a custom object. Covers indirect lookup keys, per-transaction callout caps, and what External Objects cannot do (no triggers, no validation rules, no record-triggered flows, limited reporting and search). NOT for plain REST callouts with no External Object — use apex/callouts-and-http-integrations. NOT for one-time ETL or a data migration that copies the data in — use data/data-migration-planning.4---56# Data Virtualization Patterns78Salesforce Connect lets you expose data that lives outside Salesforce9as **External Objects** — sObject-like records that look native in10the UI, in SOQL, and in Apex, but whose rows are fetched on demand11from a remote source. The data never lands in Salesforce storage.1213The promise is appealing: no ETL, no replication lag, no extra14storage cost, single source of truth. The reality has sharp edges.15External Objects are not full sObjects. Many capabilities customers16expect from a custom object — triggers, validation rules,17record-triggered flows, formula fields referencing other records,18roll-up summaries, full-text search, audit trails — are absent or19significantly restricted. Practitioners who skip the limits review20discover the gaps in production.2122This skill is a decision and configuration guide. It helps you pick23**virtualize vs replicate** for a specific data set, then walks you24through the External Object configuration that avoids the most25common production problems.2627## When virtualization is the right answer2829Virtualization is appropriate when all of the following hold:3031- The data is read-mostly from Salesforce's perspective. Writes back32 to the source are possible (OData 4.0 with writable adapter), but33 introduce more failure modes.34- The dataset is large enough that replicating it is expensive in35 storage cost or sync complexity, and the remote source is the36 authoritative system of record.37- Salesforce automation requirements are limited to display and38 cross-object reference (a Contact -> ExternalAccount lookup), not39 triggers, validation, or record-triggered flows on the external40 rows.41- The remote source can serve a request within the page-load budget42 (a few hundred milliseconds) for the typical row counts a Lightning43 page or list view will request.4445When the workload is write-heavy, automation-heavy, or latency-46sensitive, replication into a regular custom object is the right47call — even with the storage and freshness tradeoffs.4849## Adapter choices5051Salesforce Connect ships several adapters; pick by the source's52protocol and the cross-org pattern needed.5354| Adapter | Use when |55|---|---|56| OData 2.0 | Source exposes an OData 2.0 endpoint; legacy partners |57| OData 4.0 | Source exposes OData 4.0; preferred for new builds; supports writes |58| Cross-Org | Source is another Salesforce org; uses Salesforce-to-Salesforce protocol |59| Custom (Apex) | Source is REST / GraphQL / non-OData; implement `DataSource.Provider` and `DataSource.Connection` |6061The Custom (Apex) adapter is the escape hatch but carries the62ownership cost of writing the connector code, handling pagination,63mapping types, and dealing with auth refresh.6465## Recommended Workflow66671. **Confirm the use case is read-mostly and automation-light.** Validate that External Object's "no triggers, no record-triggered flows, no validation rules, no roll-up summary" limits do not block requirements. If they do, replicate instead.682. **Pick the adapter.** OData 4.0 if the source can speak it; cross-org for org-to-org; custom Apex for everything else. Avoid OData 2.0 for new builds unless the legacy partner blocks an upgrade.693. **Define indirect lookup keys.** External Objects do not have native AccountId joins; you use Indirect Lookups that join on an External Id field. Confirm the External Id is unique and indexed on the Salesforce-side parent.704. **Size the callout budget.** Each list view, related list, or page render that touches an External Object issues a callout. The per-transaction callout cap (100 sync) and per-24-hour external-object callout caps matter at scale; do not assume the limits are unlimited.715. **Test the negative paths.** Source down, source slow, source returns malformed data, auth token expired. The platform's behavior on each differs — slow source produces page-load timeouts, down source produces blank related lists, malformed data fails silently.726. **Document the practitioner contract.** Make explicit in admin / dev documentation that this object cannot have triggers, validation, or record-triggered flows. Without this, the next admin will try to add one and be confused when it is not in the picker.7374## What This Skill Does Not Cover7576| Topic | See instead |77|---|---|78| Plain REST callouts (no External Object) | `integration/named-credential-patterns` |79| One-time ETL / data migration | `data/data-migration-strategy` |80| Big Objects (append-only, async query) | `data/big-objects-patterns` |81| Change Data Capture out of Salesforce | `integration/change-data-capture-patterns` |