Skip to content

Notification Architecture That Cannot Break Your Business Logic

Separate Power Automate notification flows from business logic. One rule: notification flows read Dataverse and send email, never write back. The notification layer becomes disposable without business risk.

Alex Pechenizkiy 10 min read
Notification Architecture That Cannot Break Your Business Logic

A signing workflow processes evaluations through a multi-step chain. When a Power Automate notification flow fails, business impact is zero. When a business flow fails, the chain stops. These two outcomes are possible only because notification flows and business flows share nothing. Not a trigger. Not a variable. Not a solution import cycle.

Every notification flow in this pattern follows one rule: read Dataverse, send email, never write back. That rule is non-negotiable. It is the single architectural decision that makes the entire notification layer disposable without risk to the business.

Two-zone architecture showing business flows separated from notification flows with no coupling

The Temptation

Every maker hits this moment. A flow creates an evaluation record, assigns ownership, configures access teams, and advances the signing chain. At the end of that sequence, the natural thought is: “I should send an email to let the signer know.” So a SendEmailV2 action goes at the bottom of the flow.

It works. The signer gets the email. Ship it.

Here is what just happened. Notification logic was coupled to business logic. If the Outlook connector throttles, the business flow retries. If the email action throws an error, the flow’s error handling must now account for a notification failure in the middle of a signing chain operation. Editing the email template means editing a flow that manages business-critical state transitions. One wrong click breaks the signing chain, not the email.

This is how it starts. One email action inside one business flow. Then another. Then a condition block: “If status = Rejected, send this email. If status = Approved, send that one.” The business flow grows. The email logic grows inside it. Testing the email means running the entire business flow. Disabling the email means disabling the business flow. The two concerns are permanently fused.

This pattern shows up across many production environments. It always ends the same way: a notification change breaks business logic, and nobody understands why the signing chain stopped advancing.

The Separation Principle

Notification flows never write to Dataverse. Full stop.

This is not a best-practice recommendation. It is a structural constraint. Business flows (REV, EVL, STP series in the tag-based architecture) handle all write operations: creating records, assigning ownership, managing access teams, advancing the signing chain. Notification flows (NTF series) handle one thing: querying Dataverse and sending emails.

The separation happens at the flow level, not at the action level. There is no shared flow that branches between “do business logic” and “send notification.” Each concern lives in its own flow with its own trigger, its own run history, its own deployment cycle. To visualize this separation, architecture diagrams generated from text descriptions make the boundary obvious: business flows on one side, notification flows on the other, a wall between them.

What this means in practice:

  • A notification failure never breaks the signing chain. The business flows do not know notification flows exist.
  • Notification flows can be disabled without risk. The product owner can turn off “Signer Heads-Up” emails (NTF04) without affecting a single business operation.
  • Testing is independent. Notification flows are tested by verifying email content and recipient logic. There is no risk of accidentally advancing signing steps or reassigning record ownership.
  • Deployment is independent. Notification flows ship in their own solution import cycle. Email wording can be updated without touching the business solution.

Should Notifications Be Real-Time or Daily Digest?

In most cases, daily digests win. Real-time notifications cause email floods during bulk operations, training users to ignore them entirely. Batching notifications into a single daily email per recipient and reserving real-time delivery only for events that are rare, urgent, and triggered by a deliberate human action gives a better recipient experience.

Here is the scenario that argues against real-time delivery.

A product owner opens a review cycle for an entire department. The review-cycle flow fires and bulk-creates dozens of evaluation records, one for each employee. Each creation triggers the evaluator-assignment flow, the access-team flow, and the signing-step setup flow. The entire batch completes in under two minutes.

With real-time notifications, dozens of “Form Assigned” emails fire within seconds. A division director supervising eight employees receives eight separate emails. A senior manager overseeing three team leads receives notifications for every indirect report. The email flood trains users to ignore notification emails entirely, which is the opposite of what notifications are supposed to do.

Microsoft’s own guidance warns about this. The event-driven pattern documentation explicitly calls out the need to “prevent multiple updates from causing rapid, repeated notifications” and to “prepare a mitigation plan for unexpected spikes in event frequency.”

The daily digest is the mitigation plan.

At a fixed time on weekdays, each notification flow runs on a scheduled recurrence. It queries Dataverse for records modified in the last 24 hours, groups results by recipient, and sends one email per person. A supervisor with 8 new assignments receives a single email: “You have 8 new forms assigned” with a summary table and deep links to each record. One email. Scannable. Actionable.

The flow uses FetchXML to query and sort results by recipient, then iterates with recipient change detection to build per-person HTML bodies using Select and Create HTML Table. Every email is sent from a shared mailbox using SharedMailboxSendEmailV2, not SendEmailV2. Microsoft recommends shared senders over personal accounts for formalized notifications. The recipient sees the application name as the sender, not a developer’s personal email. For details on how the FetchXML queries work, see FetchXML in Power Automate.

The Exception: Rejection

Not every notification belongs in a digest.

When a signer rejects an evaluation, three things are true: it is rare (typically one at a time, never in bulk), it is urgent (the author needs to revise immediately), and it is always a deliberate human action. A person clicked “Reject,” typed a reason, and submitted. This is not a bulk operation that floods inboxes.

NTF05 and NTF06 are the only event-driven flows in the notification layer. NTF05 notifies the author that their form was rejected. NTF06 notifies all previous signers whose signatures are now invalidated by the rejection. Both trigger on the signing step status changing to “Rejected” and fire immediately.

This exception is not a crack in the architecture. It is proof that the architecture is honest. Blanket rules like “always batch notifications” ignore reality. The right rule is: batch by default, real-time only when the event is rare, urgent, and human-initiated.

One Flow Per Email Template

The pattern uses one notification flow per email type, not a monolithic flow with conditional branches.

Each flow sends exactly one type of email. NTF01 sends “Form Assigned” digests. NTF02 sends “Ready for Signature” digests. NTF05 sends rejection alerts. No flow contains conditional logic that selects between email templates based on input.

Why this matters:

  • Monitoring. Each flow’s run history shows exactly one email type. If “Ready for Signature” emails stop working, the right flow to inspect is NTF02. There is no scrolling through a monolithic flow’s run history trying to filter by which branch executed.
  • Debugging. A failed run points to a specific notification scenario. The flow name tells the problem domain before opening it.
  • Management. When the product owner wants to disable “Signer Heads-Up” notifications during a pilot, NTF04 is turned off. Nothing else changes. There is no risk of a condition block accidentally suppressing a different email type.
  • Naming. The tag-based naming convention makes every flow discoverable: <App> | [NTF04] Signer Heads-Up - Daily Digest. The tag tells you what it does before you open it.

Many small flows are easier to build, test, monitor, and maintain than one large flow with many branches. The monolithic approach fails the moment two email types share a partial query and someone edits the shared part without understanding both branches.

Notification Tracking

Every scheduled notification flow needs to answer one question: which records have already been notified about?

V1: 24-Hour Lookback (Simple, Fragile)

The simplest implementation uses modifiedon with a last-x-hours 24 FetchXML filter. The flow runs at 8:00 AM, queries for records modified in the last 24 hours, and sends digest emails. Simple to build. No schema changes required.

The weakness is obvious. If the flow fails at 8:00 AM Tuesday and nobody notices until Wednesday, the Tuesday records fall outside the 24-hour window. They are never notified. Microsoft documents this behavior: polling triggers do not retroactively catch missed windows when restarted.

For a launch with monitoring in place, this is acceptable. It is not resilient.

V2: Boolean Tracking Flags (Idempotent, Resilient)

The improved pattern adds boolean fields to each notifiable table. The illustrative schema below uses an app_* placeholder prefix.

TableFieldPurpose
app_personnelevaluationapp_notified_assignedAuthor notified of assignment
app_personnelevaluationapp_notified_completeAuthor notified of completion
app_evaluationsigningstepapp_notified_awaitingSigner notified of activation
app_evaluationsigningstepapp_notified_createdSigner notified of assignment
app_personnelreviewcycleapp_notified_launchedParticipants notified of launch
app_personnelreviewcycleapp_notified_closedParticipants notified of close

The flow queries for records where the flag is false, sends the notification, then sets the flag to true. If the flow fails mid-run, the unprocessed records still have false flags. The next run picks them up. No gap. No missed notifications. Microsoft recommends idempotent design exactly for this reason.

The worst case with boolean flags is a duplicate email: the flow sends the email, crashes before setting the flag, and the next run sends it again. A duplicate email is inconvenient. A missed notification during a performance review cycle is a compliance problem.

Build v1 for launch. Plan v2 from day one. Do not over-engineer the first release, but do not pretend the weakness does not exist.

Sample Flow Inventory

Here is a sample notification layer for a hypothetical performance management application, illustrating the pattern. The point is structure, not the specific flow set.

Scheduled Flows (Daily, Weekdays)

TagFlow NameTable QueriedRecipientPriority
NTF01Form Assignedapp_personnelevaluationAuthorP1
NTF02Ready for Signatureapp_evaluationsigningstepSigner (non-self)P1
NTF07Evaluation Completeapp_personnelevaluationAuthorP1
NTF10Author Reminderapp_personnelevaluationAuthorP1
NTF11Author Past Dueapp_personnelevaluationAuthorP1
NTF12Supervisor Escalationapp_personnelevaluationAuthor’s supervisorP1
NTF08Cycle Launchedapp_personnelreviewcycleAll participantsP3
NTF09Cycle Closedapp_personnelreviewcycleAll participantsP3

Real-Time Flows (Event-Driven, Immediate)

TagFlow NameTriggerRecipientPriority
NTF05Rejection to AuthorStep status changed to RejectedAuthorP1
NTF06Rejection to Previous SignersStep status changed to RejectedPrevious signersP1

Priority Tiers

  • P1 flows are must-have for go-live. Core signing workflow notifications.
  • P2 flows are important but informational. They can follow shortly after go-live.
  • P3 flows are nice-to-have. Cycle-level broadcasts can be deferred without impacting individual evaluations.

The NTF tag prefix is deliberately simple. It accepts a channel suffix (NTF-EMAIL, NTF-INAPP, NTF-TEAMS) when multi-channel delivery is added. The naming convention is ready before the channels exist.

Embedded vs Separated

Dimension Embedded (SendEmail inside business flow) Separated (Independent NTF flows)
Failure impact Notification failure can break business flow execution. If SendEmail throws an error mid-flow, the signing chain may halt. Notification failure has zero business impact. The signing chain continues regardless.
Testing Must test notifications and business logic together. Email testing triggers real state changes. Test notifications independently. No risk of advancing signing steps or reassigning ownership.
Deployment Business flows and notifications deploy together. A template change requires retesting the signing chain. Notification flows ship in their own solution. Update email wording without touching business flows.
Bulk operations A single bulk operation creating dozens of records triggers an embedded email per record. Inboxes flood. Scheduled digest batches all records into one email per recipient at the next run.
Monitoring Run history mixes business actions and email sends. Hard to isolate notification failures. Each NTF flow shows exactly one notification type. Failure points to a specific scenario.
Disabling Cannot disable notifications without disabling the business flow. Disable any single notification type without affecting business operations.

Every dimension favors separation. The only argument for embedding is convenience: it is faster to add one SendEmail action than to build a separate flow. That convenience evaporates the moment you need to debug, deploy, or disable independently.

What Makes This Work

Three environment variables drive notification flows in this pattern:

  • app_EnvironmentURL for the base URL of deep links (changes per environment)
  • app_NotificationsMailbox for the shared mailbox address used by SharedMailboxSendEmailV2
  • app_AppID for the Model-Driven App identifier used in record deep links

Every email includes a direct link to the relevant record: {EnvironmentURL}/main.aspx?etn=app_personnelevaluation&id={recordId}&pagetype=entityrecord&appid={AppID}&forceUCI=1. The appid parameter opens the record in the correct app. The forceUCI=1 parameter forces the Unified Client Interface. The user clicks the link and lands on the exact record that needs attention.

The shared mailbox is critical. Microsoft explicitly recommends sending formalized notifications from a shared sender, not from the flow owner’s personal email. Recipients know the message came from automation, and IT can manage the mailbox independently of any individual user’s account.

Build Separated or Build Twice

Most teams build notifications embedded in business flows the first time, then rebuild them separated the second time. The separation principle is the kind of decision you only need to learn once.

Notification flows read. Business flows write. The two share nothing. When one breaks, the other continues. When one deploys, the other is untouched. That is the architecture.


Spec-Driven Power Platform Series

This article is part of a series on building Power Automate solutions with specs, governance, and AI:

  1. Tag-Based Flow Architecture - How 3-letter prefixes make a flow set manageable
  2. Spec-First Development - Why specs should exist before the designer opens
  3. Notification Architecture - Notifications that cannot break business logic
  4. FetchXML in Power Automate - When OData $filter is not enough
  5. Building Solution ZIPs - The undocumented packaging guide
  6. What AI Gets Wrong - And why human correction is the point
  7. Spec-Driven Power Automate - The full story

AZ365.ai - Azure and AI insights for architects building on Microsoft. Follow Alex on LinkedIn for architecture deep dives.

Stay in the loop

Get new posts delivered to your inbox. No spam, unsubscribe anytime.

Related articles