7 min readRishi

Dual-Write vs Virtual Entities vs OData: Choosing the Right F&O–Dataverse Pattern

Finance and Operations and Dataverse often hold different parts of the same business process, but the wrong integration pattern turns a clean design into a support queue. Teams usually argue about tools before they agree on latency, ownership, and failure handling. The right choice is not the newest connector; it is the pattern whose failure mode the business can live with.

Dual-write is a product feature, not a generic sync engine

Dual-write is best when the same business concept must exist in both apps with near real-time movement. It uses Microsoft supplied and custom table maps to synchronize rows between Finance and Operations and Dataverse. That makes it attractive for customer, vendor, product, contact, and party scenarios where users expect both sides to agree quickly.

The strength is also the constraint. Dual-write assumes you are mapping Dataverse tables and Finance and Operations data entities that Microsoft has made suitable for live synchronization. You get orchestration, initial sync, table map dependencies, and error handling in the dual-write runtime, but you also inherit its opinions about shape, keys, and sequencing.

Choose dual-write when both systems need durable data. If Sales creates an account and Finance needs the customer quickly, dual-write fits. If Finance owns released products and Field Service needs product data for work orders, dual-write can fit. If the data is only needed for occasional lookup, dual-write usually creates unnecessary duplication and operational noise.

Virtual entities are live projection for read-mostly scenarios

Virtual entities expose external data in Dataverse without storing the rows as normal Dataverse records. For Finance and Operations scenarios, the practical benefit is a live view into Finance and Operations data from model-driven apps, lookups, and forms. Users see current values without a sync backlog, storage growth, or reconciliation process.

That sounds ideal until someone tries to use virtual data like local data. Virtual entities are slower than local Dataverse tables because each read crosses a boundary. Query support can be limited by the provider. Offline, auditing, rollup, and workflow behavior may not match normal tables. Writes are possible only when the provider and entity support them, and even then you should treat the pattern as read-mostly.

Use virtual entities for reference data and operational visibility. Examples include showing inventory availability, purchase order status, credit hold state, or invoice history inside a customer service app. Do not use them for high-volume grids that users filter constantly, or for transactional logic that requires Dataverse plug-ins to update the projected row.

OData and DMF are integration tools with different clocks

Finance and Operations OData endpoints are useful for service-to-service operations where the caller needs a relatively direct API. The Data Management Framework, usually called DMF, is better for file and batch movement. Both are valid, but they solve different timing problems.

OData fits moderate volume, API-driven integration. A Dataverse plug-in or Azure Function can call Finance and Operations to create a sales order, validate a customer, or retrieve a status. The caller must handle throttling, retries, authentication, and partial failures. For large loads, OData can become expensive because every row behaves like an API transaction.

DMF fits bulk import and export. It is deliberately asynchronous and operational. You stage data packages, run jobs, monitor execution, and handle errors in batches. That is exactly what you want for nightly pricing updates, large customer migrations, historical invoices, or master data loads where a one-minute latency target is fake precision.

{
  "integration": "finance-price-export",
  "pattern": "dmf-batch",
  "schedule": "0 2 * * *",
  "retryPolicy": {
    "maxAttempts": 3,
    "backoffSeconds": 300
  },
  "owner": "finance-platform"
}

Latency should be designed before fields are mapped

Latency is not just a performance number. It defines user expectation, support behavior, and the kind of reconciliation you need. If a sales user edits an account and Finance is expected to block invoicing within seconds, you need a near real-time pattern. If the warehouse only needs updated discount groups before tomorrow morning, a batch is safer and easier to govern.

PatternBest latencyDirectionVolume fitPrimary failure mode
Dual-writeSeconds to minutesBidirectional or configured one-way mapsMedium operational dataMap errors, dependency failures, stuck sync
Virtual entitiesLive read at request timeUsually external to DataverseRead-heavy, low to moderate gridsSlow reads, provider limits, unavailable source
ODataRequest timeUsually caller-directedLow to moderate transactionsAPI throttling, partial transaction failure
DMF batchMinutes to hoursImport or export jobHigh volume and migration loadsBatch rejection, staging errors, delayed correction

The trap is forcing every integration into the lowest possible latency. Fast integration is not automatically better. It is more coupled, more expensive to operate, and less tolerant of source system downtime.

Ownership decides the direction of truth

Before choosing a connector, write down the system of record for each field. Not each table: each field. Customer name may be mastered in Dataverse while credit limit is mastered in Finance and Operations. Product description may come from Finance, while sales collateral lives in Dataverse. This field-level ownership prevents circular updates and makes support decisions obvious.

Use one-way maps where possible. Bidirectional sync sounds democratic, but it increases conflict paths. If both systems can update the same attribute, decide which update wins, how conflicts are detected, and who owns correction.

Separate reference from transaction data. Reference data can tolerate scheduled updates or projection. Transaction data usually needs clear command ownership. For example, creating an order in Dataverse and creating a sales order in Finance and Operations are not the same event unless the business accepts the same validation rules and failure handling.

Design idempotency early. Every integration eventually retries. Use stable alternate keys, external correlation IDs, and status tables so a retry updates the same logical request instead of creating another row.

CREATE TABLE IntegrationRequest (
    CorrelationId nvarchar(100) NOT NULL PRIMARY KEY,
    SourceSystem nvarchar(40) NOT NULL,
    TargetEntity nvarchar(80) NOT NULL,
    TargetKey nvarchar(100) NULL,
    Status nvarchar(30) NOT NULL,
    LastError nvarchar(max) NULL
);

Failure handling is where architecture becomes real

Dual-write failures are visible in dual-write monitoring and often relate to missing dependencies, invalid mappings, or data validation differences. The support model is map-centric: fix the data or mapping, then replay. You need owners who understand both apps and can read the error, not just restart the job.

Virtual entity failures are user-facing. If Finance and Operations is slow or unavailable, the Dataverse form may be slow or empty. That is acceptable for visibility scenarios, but dangerous for core processes. A good virtual entity design includes fallback messaging and avoids placing the projected data in the middle of a save transaction.

OData failures belong to the caller. If an Azure Function creates a customer and then fails while writing the Dataverse status, you own the compensation logic. For DMF, the failure is usually batch operational: rejected package, staging validation, or records in error. That requires monitoring, reprocessing, and business-friendly error reports.

$job = @{
  Name = "NightlyCustomerExport"
  Pattern = "DMF"
  MaxRetry = 3
  AlertChannel = "FinanceOps"
}

$job | ConvertTo-Json

The practical selection rule is boring and reliable

Start with the business question. If both systems must own and act on the data quickly, evaluate dual-write first. If Dataverse users only need to see Finance and Operations data, prefer virtual entities. If another service is commanding a specific transaction, use OData with idempotent design. If the requirement is volume, reconciliation, or scheduled movement, use DMF.

Do not let a pattern solve a problem you have not named. Dual-write is powerful when the data model fits. Virtual entities are elegant when users can tolerate live dependency. OData is precise when calls are small and controlled. DMF is dependable when volume matters more than immediacy. The senior move is choosing the failure mode before choosing the connector.

Keep reading

Newsletter

New posts, straight to your inbox

One email per post. No spam, no tracking pixels, unsubscribe anytime.

Comments

No comments yet. Be the first.