Power PlatformServer-side business logic with Dynamics 365 plugins
Plugins run your business rules on the server, where they can't be bypassed. Here's how they work, when to use sync vs async, and how to keep them reliable.
Some rules must always hold, no matter which app, integration or import touches the data. That’s what plugins are for. A plugin is server-side code that runs inside the Dataverse event pipeline in response to operations like create, update, delete or a custom message — the one place where logic genuinely can’t be bypassed by the client.
If a business rule can be enforced in a form, that’s fine for convenience. But if it must be true of every record however it was created — an API call, a bulk import, another app — then it belongs in a plugin.
The event pipeline
Dataverse processes each operation through a pipeline with distinct stages, and registering a plugin at the right stage is most of what it takes to write predictable code:
- Pre-validation runs before the main transaction, even before security checks in some cases — useful for validation that should happen very early.
- Pre-operation runs inside the transaction, before the record is written. This is where you adjust incoming data — defaulting values, normalising fields — so the change is saved with your modifications.
- Post-operation runs after the record exists. This is where you react: create related records, roll up totals, or raise an event for downstream integration.
Knowing which stage you’re in, and what data is available at each, prevents a whole class of subtle bugs.
Synchronous or asynchronous?
This is the decision that most affects how a plugin behaves and how users experience it.
Synchronous plugins run inside the user’s transaction. Use them when the work must complete — and be able to block or roll back the operation — before the user moves on. Enforcing a validation rule that should stop a save is the classic case. The trade-off is that heavy synchronous work slows the user down and, if it calls something slow or unreliable, risks timeouts and failed saves.
Asynchronous plugins run in the background shortly after the operation completes. They’re the right choice for work that doesn’t need to hold up the user: sending a notification, calling an external system, kicking off a longer process. Anything that reaches outside Dataverse should almost always be asynchronous, both to keep the platform responsive and because external calls fail in ways you don’t want tied to a user’s save.
A good rule of thumb: if the user must not proceed unless the work succeeds, go synchronous; otherwise go asynchronous.
Custom APIs
When you need a named, reusable operation — say “approve order” with its own inputs and outputs — a custom API is often far cleaner than overloading a field update to trigger behaviour. It gives you an explicit contract that apps, flows and integrations can call directly, with the business logic implemented as a plugin behind it.
This keeps your logic discoverable and intentional rather than hidden inside a side effect of an update. It also makes the boundary between “what the operation does” and “how it’s implemented” clear, which pays off every time someone new has to understand the system.
Keeping plugins reliable
Good plugins are small, focused and defensive:
- Register them narrowly. Fire on the specific messages, and where possible the specific columns, you actually care about. A plugin that runs on every update of every field is both slow and a magnet for bugs.
- Guard against loops. A plugin that updates the same record it fires on can trigger itself. Use the execution depth and careful conditions to prevent runaway recursion.
- Handle missing data. Don’t assume a field or related record is present; check, and fail with a clear, actionable error message rather than an unhandled exception.
- Keep them fast. Especially for synchronous plugins, every millisecond is added to the user’s save. Do the minimum, and push heavier work to async.
Deliver them like real code
Because plugins are code, they belong under source control and travel through the same solution pipeline as everything else. That means they’re versioned, reviewed and deployed predictably — never hand-registered directly against production. Registering plugin steps as part of the solution keeps the whole configuration reproducible across environments.
Automated tests are worth the effort too. Isolating the business logic from the plugin’s execution context lets you unit-test the rules directly, so you catch regressions before they reach an environment.
Wrapping up
Treated this way, plugins become the dependable backbone of a solution: the rules of the business, enforced on the server where it counts, no matter how a record is created or changed. Get the pipeline stage right, choose sync or async deliberately, expose named operations as custom APIs, keep each plugin small and defensive, and ship it all through proper ALM — and you have logic you can trust and maintain for the long haul.
Want to talk through something like this for your own environment? Get in touch.