Almanac
Microsoft/dataversePower Platform

Consultant KB for Microsoft Dataverse, the data layer under Dynamics 365 and the Power Platform: data model, security model, business logic, APIs and integration, search and queries, analytics and Fabric, ALM and solutions, administration, governance and compliance, and Dataverse as an agent data platform, plus cross-cutting decision guides. Implementation notes, configuration decisions and the gotchas that bite on real projects. Populated by the daily author agent from the Dataverse release plans, docs repo and product blog, plus the author's own consultant notes.

feature-plug-in-vs-flow-vs-business-rule.mdv1 · history
CurrentApplies to AllUpdated last monthSource Microsoft Learn

What it does

Three places to put business logic against Dataverse, distinguished by when they run, what they can reach and who can maintain them. Business rules are declarative and run on the form and at the table level. Plug-ins are .NET code registered on the platform event pipeline and run inside the database transaction. Cloud flows run asynchronously outside the transaction and reach anything a connector reaches.

Key facts

  • Microsoft's stated order of preference is declarative first, code when declarative doesn't meet the requirement. That's the documented position, and it's right more often than developers like.
  • Business rules are limited to 150 per table. Beyond that Microsoft documents performance degradation and tells you not to.
  • Business rules defined at table scope apply to both model-driven and canvas apps using that table. Form-scope rules apply only where you put them.
  • Plug-ins registered synchronously run inside the transaction, so throwing an exception rolls the operation back. Nothing else on this list can do that.
  • Plug-ins run in a sandboxed isolation mode with a two-minute execution limit per registered step, and outbound access restricted to HTTP/HTTPS on standard ports.
  • Cloud flows on the Dataverse connector trigger after the operation has committed. They cannot prevent a write, only react to one.
  • Low-code plug-ins let you write Power Fx expressions that run server-side as instant or automated plug-ins — a middle option between business rules and C#.
  • Real-time (synchronous) workflows still exist and still run in-transaction, but Microsoft's direction for new build is plug-ins for synchronous logic and flows for asynchronous.
  • Bypassing custom logic is supported for specific callers via documented bypass parameters — useful during data migration, and worth knowing exists before someone disables logic manually.

When to use / skip

The rule I'd give a colleague: if it must be true before the row is saved, it's a plug-in. If it just needs to happen afterwards, it's a flow. If it's a field being hidden, required or defaulted on a form, it's a business rule and you should stop thinking about it.

The most common mistake in the field is using a flow to enforce a rule. A flow fires after commit, so the bad data is already in the table and any downstream integration has already seen it. Then somebody adds a second flow to correct it, and now you have two writes, an audit trail full of noise and a race condition. If the requirement contains the word "must" or "prevent", it's transactional and a flow is the wrong tool no matter how much easier it is to build.

The second most common mistake runs the other way — a developer writes a plug-in for something two business rules would do, and three years later nobody on the client's team can change it. Maintainability is a real design input, not a soft one.

Configuration decisions

  • Does this need to prevent the save, or react to it? That question alone settles most of the argument.
  • Who supports this in two years — the client's Power Platform team, a partner, or nobody? Code you can't hand over is a liability disguised as a solution.
  • Which pipeline stage and mode a plug-in registers on: pre-validation, pre-operation or post-operation, synchronous or asynchronous. Getting this wrong causes behaviour that's very hard to debug later.
  • Whether low-code plug-ins close the gap — server-side logic without a C# build pipeline is often exactly what a client with makers and no developers needs.
  • Whether the volume justifies moving processing out of flows entirely. Moving or processing large numbers of Dataverse records is documented as a case for custom actions rather than complex logic in a flow.

Gotchas

  • Business rules run client-side on the form as well as server-side at table scope, and the two scopes behave differently. A rule that "works" in the app may not apply to an API write at all.
  • Flows fire on commit, which means a plug-in and a flow reacting to the same event will interleave in a way that isn't obvious and isn't guaranteed.
  • A synchronous plug-in that calls an external service puts that service inside your save transaction. When it's slow, saving a record is slow, and the two-minute limit becomes a real ceiling.
  • Elastic tables have no multi-record transaction, so the rollback guarantee developers rely on from standard tables doesn't hold there.
  • Logic scattered across all three mechanisms on the same table is the state most inherited systems are in. Finding it requires reading solution components, not asking the client.

Consultant notes

  • Write the rule down as an architecture principle in the first workshop: transactional integrity means plug-in, orchestration and integration means flow, form behaviour means business rule. Then enforce it in code review.
  • Push back hard on "we'll just do it in a flow" when the requirement is a validation. It's the cheapest thing to fix at design time and one of the most expensive afterwards.
  • Ask who will own the code before agreeing to write any. A client with no developers and a partner-built plug-in estate is a support problem you're creating.
  • Low-code plug-ins are the option most clients haven't heard of and often the right one. Bring them into the conversation rather than defaulting to a binary.

Worth revisiting as low-code plug-ins mature — they keep taking territory from both sides of this argument

Was this accurate?