Anti-corruption layers for Dynamics 365 integrations
By Emil Björk · Microsoft business apps consultant, Gothenburg
How anti-corruption layers protect Dynamics 365 from external system model leakage — translation patterns, when to apply ACL, and the maintenance discipline.
When Dynamics 365 integrates with external systems, those systems have their own domain models, terminology, and quirks. Without intentional design, those external models leak into Dynamics — concepts named oddly, fields that don't match Dynamics's semantics, business logic intertwined. An anti-corruption layer (ACL) is the architectural pattern that prevents this leakage: a translation boundary that lets each side keep its own model clean.
The problem.
- External system A uses "Account" for what Dynamics calls "Customer."
- External system B has status codes "1, 2, 3, X, Y" with non-obvious meaning.
- External system C's date format differs.
- Each is a small thing; together they corrupt the model.
Without ACL, Dynamics ends up with foreign concepts; future maintenance pays the cost.
What an ACL provides.
- Translation — external concepts mapped to Dynamics concepts.
- Isolation — external system changes don't ripple into Dynamics.
- Defensive validation — external data validated before crossing.
- Abstraction — Dynamics doesn't depend on external's specifics.
The investment is real; the long-term maintenance benefit is greater.
Where to put the ACL.
- Azure Function — common Microsoft pattern.
- Logic App — for some scenarios.
- Custom service in App Service.
- API Management policies — for inbound translation.
- Plug-in — for simple cases.
For complex translations, dedicated services typically.
ACL responsibilities.
- Receive external system data.
- Validate — required fields, formats.
- Translate — to Dynamics model.
- Enrich — add Dynamics-specific defaults.
- Forward — to Dynamics API.
- Translate response back if needed.
The same in reverse for outbound.
Translation patterns.
- Field mapping —
external.CustomerId → dynamics.AccountNumber. - Value mapping —
external.Status "1" → dynamics.statecode 0. - Structural transformation — flatten / unflatten.
- Lookup translation —
external.CountryCode "US" → dynamics.Country reference.
Each translation is a documented rule.
Validation rules.
- Required fields present.
- Format valid — email, date, numbers.
- Value ranges — within expected.
- Business rules — domain validation.
Catch problems at the boundary; Dynamics doesn't process bad data.
Bidirectional ACL.
- Inbound translation (external → Dynamics).
- Outbound translation (Dynamics → external).
- Both managed in the ACL.
Symmetric translation; consistent model isolation.
When ACL pays back.
- Integration with legacy systems — old systems have unique models.
- Multiple similar external systems — multiple insurers, multiple banks; each slightly different.
- External systems that change frequently — ACL absorbs the change.
- Different teams own each system — ACL is the contract boundary.
When ACL is overkill.
- Simple, well-defined integration — direct mapping suffices.
- One-time data migration — overhead exceeds benefit.
- Low volume — manual quirks tolerable.
For most production integrations, some ACL exists even if minimal.
Implementation example.
External system "ProductFulfillment" sends shipments to Dynamics:
External payload:
{
"shipRef": "SHIP-12345",
"soNum": "SO-99",
"stat": "S",
"shipDate": "20261110"
}
Translated to Dynamics:
{
"shipmentTrackingNumber": "SHIP-12345",
"salesOrderId": "lookup-by-name(SO-99)",
"statusCode": 3, // Shipped
"shipmentDate": "2026-11-10T00:00:00Z"
}
The ACL does this translation; Dynamics never sees the foreign vocabulary.
Schema versioning
External system schemas evolve:
- ACL versioned independently.
- Multiple versions can coexist.
- Migration window for upstream callers.
Versioning enables schema evolution without breaking consumers.
Error handling at ACL.
- Validation errors — return to source; not propagated.
- Translation errors — log and alert; retry or dead-letter.
- Dynamics errors — translated back to source-friendly message.
The ACL prevents cryptic errors from reaching either side.
Monitoring ACL health.
- Throughput — messages translated.
- Validation failures — what's coming in bad.
- Latency — translation time.
- Errors — type and rate.
Standard observability; treats ACL as production service.
Common pitfalls.
- No ACL. External model leaks into Dynamics; foreign concepts proliferate.
- ACL as thin pass-through. Doesn't translate; just forwards. No protection.
- Hard-coded mappings. Should be configuration; making changes requires code.
- No validation. Bad data reaches Dynamics.
- Stateful ACL. State should typically live in Dynamics or external; ACL is stateless translation.
- Performance bottleneck. Heavy translation becomes throughput limit.
Patterns and frameworks.
- AutoMapper (.NET) — declarative object mapping.
- JSONata — JSON transformation language.
- JOLT — Java-based.
- Custom in TypeScript / C# — full control.
Choose based on team skill and translation complexity.
Testing.
- Unit tests — translation correctness.
- Property-based tests — round-trip preservation.
- Integration tests — with real external system.
- Performance tests — high-volume.
ACL is critical infrastructure; test like it.
Documentation.
- Field mapping documented.
- Value mappings referenced.
- Validation rules explicit.
- Error meanings explained.
Documentation enables maintenance years later.
Strategic positioning
Anti-corruption layers represent architectural maturity. Teams new to integration often skip ACL — direct mappings seem simpler. Teams with experience know the long-term cost of model leakage; they invest in ACL upfront.
For architects:
- Identify boundaries where ACL matters.
- Choose appropriate technology per boundary.
- Build versioning and observability.
- Document translations.
The investment pays back through:
- Cleaner Dynamics model.
- External system changes absorbed.
- Easier debugging.
- Lower maintenance over years.
Not every integration needs heavy ACL; every meaningful integration benefits from some level of intentional translation boundary. Design for the long term.
Related guides
- Idempotency in Dynamics 365 integrationsWhy integrations must be idempotent — patterns for safe retries, deduplication, correlation IDs.
- Polling vs push patterns for Dynamics 365 integrationsHow to choose between polling and push integration patterns for Dynamics 365 — trade-offs, performance, freshness, and when each fits.
- Event-driven architecture for Dynamics 365How to design event-driven integrations around Dynamics 365 — event sources, brokers, consumers, and the patterns that keep systems loosely coupled.
- Integration patterns for Dynamics 365 CRMThe standard integration patterns for CRM-side Dynamics 365 — webhooks, Dataverse plug-ins, Service Bus integration, virtual tables, and pull vs push.
- Eventually consistent integrations with Dynamics 365How to design and operate eventually-consistent integrations — consistency vs availability trade-offs, conflict resolution, and the UX implications.
Browse every guide in Integrations or just Architecture patterns.
Integrations architecture
Was this helpful?
Signals which guides land and which need work. No account, no comment box — corrections go through the contact page.
Spot something wrong or want a topic covered? Send a correction or a topic request — both are welcome.