Both platforms exist because they solve different problems with different architectures. The work is not choosing between them; it is connecting them well. This page covers the five integration patterns that make coexistence work and the anti-patterns that break it. Getting this right is what separates enterprises that have data platforms from enterprises that get value from them.
The Closed Loop
The EDP and operational platform form a continuous cycle. Data flows from source systems through the event backbone, gets processed independently by both platforms, and feeds back in both directions.
graph LR
A["Source Systems"] --> B["Event Backbone<br/>(Kafka, Pub/Sub, Event Hubs)"]
B --> C["Operational Platform<br/>(live process)"]
B --> D["EDP Ingestion<br/>(bronze)"]
D --> E["Curated Layers<br/>(silver → gold)"]
E --> F["Data Products"]
F --> G["Serving Layer<br/>(APIs, feature store, reverse ETL)"]
G --> C
C --> |"operational events"| B
Every arrow in this diagram represents a contract. The event backbone defines event schemas. The EDP defines data product schemas. The serving layer defines API contracts. When teams treat these as informal, ad-hoc connections, the loop breaks. When they formalize them, the loop compounds value over time.
Pattern 1: Event-Driven Ingestion
Operational systems emit domain events as part of their normal operation: order placed, payment processed, claim submitted, account opened. These events flow through the backbone. The EDP subscribes to the events it needs.
graph LR
A["Order Service"] --> |"OrderPlaced"| B["Event Backbone"]
C["Payment Service"] --> |"PaymentProcessed"| B
D["Claims Service"] --> |"ClaimSubmitted"| B
B --> E["EDP Ingestion"]
E --> F["Bronze Layer<br/>(raw events, full fidelity)"]
Operational systems are unaware of the EDP. They emit events because events are part of their domain model, and the EDP subscribes. Operational systems never need to change when the EDP adds new consumers, new transformations, or new data products.
This is the most important pattern. If you get nothing else right, get this one right.
| Concern | Guidance |
|---|---|
| Event schema | Owned by the producing service. Published to a schema registry. |
| Delivery guarantee | At-least-once. The EDP handles idempotency. |
| Retention | The backbone retains events long enough for the EDP to consume. The EDP provides long-term historization. |
| Ordering | Per-entity ordering (partition key) is usually sufficient. Global ordering is rarely needed and expensive. |
Managed variant: zero-ETL and mirroring. Cloud vendors now sell this pattern pre-built: zero-ETL integrations and mirroring replicate an operational database into the analytical store continuously, with no pipeline to write. It is a reasonable default when a single vendor-aligned source needs to reach the warehouse and no other consumer needs the change stream. Build the CDC-plus-backbone version when multiple consumers need the events or when contracts and quality gates must apply before data lands. The trade-offs are covered in Open Formats and Catalogs.
Pattern 2: Reverse ETL / Operational Sync
The EDP computes things that operational systems cannot compute on their own: Customer 360 profiles, risk scores, segmentation labels, propensity models, lifetime value calculations. These computations require joining data across domains, applying complex business logic, and processing historical data, none of which operational systems are designed for.
Selected datasets are pushed back to operational systems via reverse ETL.
graph LR
A["EDP Gold Layer"] --> B["Customer 360<br/>Risk Scores<br/>Segmentation"]
B --> C["Reverse ETL"]
C --> D["CRM<br/>(enriched profiles)"]
C --> E["Marketing Platform<br/>(segments)"]
C --> F["Underwriting System<br/>(risk scores)"]
The EDP is the source of truth for the computed data; the operational system receives a copy. If the CRM shows a customer’s risk score, that score was computed by the EDP and pushed to the CRM. The CRM does not compute it.
| Concern | Guidance |
|---|---|
| Freshness | Batch is fine for most use cases (hourly, daily). Near real-time reverse ETL adds complexity; justify it with a real business requirement. |
| Conflict resolution | The EDP-computed field overwrites the operational field. There is no merge. If there is ambiguity about which system owns a field, you have a governance problem, not a technical one. |
| Scope | Push only what the operational system needs. Do not replicate the entire gold layer into every downstream system. |
Pattern 3: Feature Serving
ML models in production need features at inference time. The EDP computes these features offline (batch feature engineering), but serving them at inference-time latency requires a different infrastructure.
graph LR
A["EDP Data Products"] --> B["Offline Feature Store"]
B --> C["Feature Engineering<br/>(batch)"]
C --> D["Online Feature Store<br/>(Redis, Bigtable, DynamoDB)"]
D --> E["Model Serving<br/>(inference)"]
F["Real-Time Events"] --> D
The EDP computes, the serving layer delivers, and the EDP is never queried at inference time. A model that needs a customer’s 90-day transaction summary does not query BigQuery in the request path. That summary was precomputed by the EDP, materialized to an online store, and is fetched in milliseconds when the model needs it.
| Aspect | Offline (EDP) | Online (Serving) |
|---|---|---|
| Latency | Minutes to hours | Milliseconds |
| Compute | Batch processing, SQL, Spark | Key-value lookup |
| Update frequency | Scheduled (hourly, daily) | Precomputed or streaming |
| Consumer | Model training, batch scoring | Real-time inference endpoints |
Pattern 4: API-Mediated Access
Data products are not tables you give people credentials to. In cross-platform scenarios, data products are exposed through APIs.
graph LR
A["EDP Data Products"] --> B["API Layer<br/>(REST, GraphQL)"]
B --> C["Operational App A"]
B --> D["Operational App B"]
B --> E["Partner System"]
B -.-> |"auth, rate limiting,<br/>caching, versioning"| B
The API layer decouples consumers from the EDP’s internal structure. When the EDP migrates from one storage engine to another, refactors its medallion layers, or restructures its data products, the API contract stays stable. Consumers call APIs, not query the lakehouse.
| Concern | Guidance |
|---|---|
| Authentication | OAuth2 / service accounts. Never shared credentials. |
| Rate limiting | Protect the EDP from noisy consumers. Different tiers for different use cases. |
| Caching | Cache aggressively for data that changes infrequently (reference data, daily aggregates). |
| Versioning | API versioning is non-negotiable. Breaking changes get a new version, not a silent update. |
Pattern 5: Shared Reference Data
Master data (customer, product, location, organizational hierarchy) is consumed by both platforms. The source of truth is typically an MDM system or a designated source system. Both the EDP and the operational platform consume from that same source.
graph TD
A["MDM / Source System<br/>(authoritative source)"] --> B["Event Backbone"]
B --> C["Operational Platform<br/>(current version)"]
B --> D["EDP<br/>(historized, versioned)"]
One authoritative source, two consumption patterns. The operational platform uses the current version of a customer record for live transactions. The EDP historizes every version for analytics, regulatory reporting, and audit. Neither platform tries to be the master.
| Concern | Operational Platform | EDP |
|---|---|---|
| What it stores | Current state only | Full history (SCD Type 2 or append-only) |
| Why | Transactions need the latest record | Analytics and compliance need point-in-time accuracy |
| Update pattern | Overwrite on change | Append new version, close previous |
| Latency | Immediate propagation | Minutes to hours is acceptable |
Integration Anti-Patterns
These are the patterns that look like shortcuts and end up as long-term liabilities.
Direct database connections between platforms. When the operational platform queries the EDP’s database directly, or vice versa, you have created a coupling that makes independent evolution impossible. Schema changes in one platform break the other. Maintenance windows collide. Performance degrades unpredictably.
Operational systems querying the EDP for live decisions. The EDP is optimized for analytical throughput, not transactional latency. When an operational system queries the EDP in a user-facing request path, you get inconsistent response times, query queuing during peak analytical workloads, and an architecture that is fragile under load. Use a serving layer (Pattern 3, Pattern 4).
Batch file transfers as the primary integration mechanism. CSV files on SFTP servers, shared NFS mounts, nightly database dumps. These are not integration patterns; they are the absence of one. No schema contracts, no delivery guarantees, no observability, no error handling beyond “the file didn’t show up.”
Shared databases between operational and analytical workloads. The single worst architectural decision in enterprise data. Operational workloads need transactional consistency, low latency, and high concurrency. Analytical workloads need full scans, complex joins, and high throughput. Running both against the same database means neither gets what it needs. This is the problem the EDP exists to solve.