6 min readRishi

Business Rules vs Power Fx vs Plug-ins: Where to Put Dataverse Logic

Dataverse projects rarely fail because a rule was impossible to implement. They fail because the rule was implemented in five places with five different behaviors. The hard part is not writing logic; it is deciding where that logic belongs so users, integrations, and administrators see the same outcome.

Business rules are best for simple, visible data behavior

Business rules are the first place to look for straightforward field logic. They can set values, show or hide fields, make fields required, validate data, and provide business-friendly messages. They are no-code, solution-aware, and readable by functional consultants.

The catch is scope. A business rule can run on a form, and depending on configuration it can also apply at the table level for server-side enforcement. That distinction matters. If the rule only runs on the form, imports and API updates can bypass it. If the rule must protect the data, make sure it is designed and tested as server-side logic.

Use business rules for simple field-level decisions. Examples include requiring a reason when status changes, defaulting a classification from a type, or showing fields based on category. Avoid using them to coordinate multiple tables or implement long decision trees. When a business rule becomes a maze, it is no longer maintainable just because it is no-code.

Power Fx is a family of options, not one runtime

Power Fx shows up in multiple Dataverse places: formula columns, calculated behavior, command buttons, and low-code plug-ins. Those are not interchangeable. A formula column computes a value. A command formula supports user-driven actions. A low-code plug-in runs server-side on a defined operation or event.

The practical question is when the formula must run. If the value is derived and should always be calculated from other fields, a formula column may be enough. If the user clicks a command and the rule is explicit, a command formula can be elegant. If the logic must run on create or update regardless of entry point, a low-code plug-in is a better fit.

If(
    Self.Selected.Item.'Estimated Revenue' > 50000,
    Notify("Executive review is required", NotificationType.Warning),
    Patch(
        Opportunities,
        Self.Selected.Item,
        { 'Review Required': false }
    )
)

The snippet is fine for a command experience. It is not a substitute for server-side enforcement. If executive review is mandatory for all high-value opportunities, put that rule where imports and API calls cannot skip it.

JavaScript form scripts should improve experience, not own truth

Classic JavaScript form scripts still have a place in model-driven apps. They can react to onLoad, onChange, and onSave, use the formContext API, call client APIs, and create a responsive user experience. They are useful for dynamic UI behavior that business rules cannot express cleanly.

But JavaScript is client-side. It runs in the browser and depends on the form. It does not automatically protect data changed through integrations, imports, background jobs, or other apps. That makes it a poor home for rules that define truth.

Use form scripts for experience orchestration. Examples include filtering lookups based on current form state, showing contextual notifications, controlling tabs, or performing lightweight client-side warnings. Pair them with server-side validation when the rule matters beyond the screen.

function onPriorityChange(executionContext) {
  const formContext = executionContext.getFormContext();
  const priority = formContext.getAttribute("prioritycode")?.getValue();

  if (priority === 1) {
    formContext.ui.setFormNotification(
      "High priority cases require a response plan.",
      "WARNING",
      "priority-response-plan"
    );
  } else {
    formContext.ui.clearFormNotification("priority-response-plan");
  }
}

C# plug-ins are for durable server-side rules

C# plug-ins remain the strongest option for rules that must be enforced across every entry point. They run in the Dataverse pipeline and can participate in transactions. They are appropriate for complex validation, cross-table updates, controlled external calls, and logic that needs professional engineering practices.

That power has a cost. Plug-ins require code ownership, deployment discipline, tracing, and performance review. A synchronous plug-in runs on the user wait path, so it must be fast. A badly written plug-in can slow every save or create recursive updates.

The right plug-in is small and explicit. It validates the message, table, stage, and changed attributes. It uses pre-images or post-images when needed. It avoids unnecessary service calls and traces decision points. Use C# when the rule deserves that rigor.

The decision table should be used before implementation

Logic locationBest forAvoid whenPortabilityEnforcement strength
Business ruleSimple field behavior and validationLogic spans many tablesGood inside DataverseMedium to strong by scope
Power Fx formula columnDerived valuesSide effects are requiredGood inside DataverseStrong for calculation
Power Fx commandUser-triggered actionsRule must run for importsApp-specificWeak to medium
Low-code plug-inBusiness-readable server logicFormula becomes complexGood with solutionsStrong
JavaScript form scriptResponsive form experienceRule defines data truthForm-specificWeak
C# plug-inComplex transactional rulesSimple UI-only behaviorStrong with ALMStrong

This table prevents tool-driven design. Start with scope and enforcement. Then choose the simplest tool that meets both.

Maintainability beats clever placement

The most maintainable architecture is usually layered. Use business rules for obvious form and field behavior. Use formula columns for derived values. Use JavaScript only where the browser experience needs help. Use low-code plug-ins for concise server-side rules that business owners can understand. Use C# for rules that need advanced code, transaction control, or integration discipline.

Do not duplicate the same rule casually. If a form script warns the user and a plug-in enforces the rule, make that relationship intentional. The script improves experience; the plug-in protects truth.

Name rules after outcomes. “Require closure reason” is better than “Case business rule 3”. Future support depends on names as much as code.

Document the owner. A functional rule owned by operations should not be hidden in JavaScript that only developers review. A financial validation should not live in an unmanaged formula edited directly in production.

Performance is part of correctness

Logic placement affects performance. Business rules and form scripts can slow forms if they create too much client behavior. Formula columns can affect views and queries depending on complexity. Plug-ins can slow saves and transactions. Flows can introduce eventual consistency and delayed side effects.

Treat performance as a design requirement, not a test cleanup item. If the rule must block a save, make it server-side and fast. If the rule can happen later, decouple it. If the rule only helps the user decide, keep it on the form and do not pretend it protects the database.

Dataverse gives you many places to put logic because business apps need different kinds of behavior. The senior choice is not always C#, and it is not always low-code. The senior choice is the location that makes the rule visible, enforceable, portable, and cheap to support two years from now.

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.