Notification Architecture That Cannot Break Your Business Logic
Separate Power Automate notification flows from business logic. 14 flows, zero write operations, daily digests over real-time floods.
Our signing workflow processes evaluations through a 5-step chain. When a Power Automate notification flow fails, zero business impact. 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 even a solution import cycle.
I built 14 notification flows for the Meridian performance management system. Every one of them follows one rule: read Dataverse, send email, never write. That rule is non-negotiable. It is the single architectural decision that made the entire notification layer disposable without risk to the business.
The Temptation
Every maker hits this moment. You are building a flow that creates an evaluation record, assigns ownership, configures access teams, and advances the signing chain. At the end of that sequence, you think: “I should send an email to let the signer know.” So you drag a SendEmailV2 action to the bottom of the flow.
It works. The signer gets the email. Ship it.
Here is what you just did. You coupled your notification to your business logic. If the Outlook connector throttles, your business flow retries. If the email action throws an error, your flow’s error handling must now account for a notification failure in the middle of a signing chain operation. If you need to change the email template, you are editing a flow that manages business-critical state transitions. One wrong click and you break 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.
I have seen this pattern in production at three different organizations. 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. This is a structural constraint. In Meridian, the 10 business flows (REV, EVL, STP series) handle all write operations: creating records, assigning ownership, managing access teams, advancing the signing chain. The 14 notification flows (NTF-EMAIL 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. If you want to visualize this separation, architecture diagrams generated from text descriptions make the boundary immediately 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. Sarah Chen, our product owner, can turn off “Signer Heads-Up” emails (NTF-EMAIL-04) without affecting a single business operation.
- Testing is independent. I test notification flows by verifying email content and recipient logic. No risk of accidentally advancing signing steps or reassigning record ownership.
- Deployment is independent. Notification flows ship in their own solution import cycle. Update email wording without touching the business solution.
Should You Send Real-Time or Daily Digest Notifications?
In most cases, daily digests win. Real-time notifications cause email floods during bulk operations, training users to ignore them entirely. Batch notifications into a single daily email per recipient, and reserve real-time delivery only for events that are rare, urgent, and triggered by a deliberate human action.
Here is the scenario that killed real-time notifications for us.
Sarah Chen opens a review cycle for the Engineering department. REV01 fires and bulk-creates 47 evaluation records - one for each engineer. Each creation triggers EVL01 (author assignment), EVL03 (access team configuration), and EVL04 (signing step creation). The entire batch completes in under two minutes.
With real-time notifications, 47 “Form Assigned” emails fire within seconds. James Wright, a division director supervising 8 engineers, receives 8 separate emails. A senior manager overseeing 3 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. Their 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 8:00 AM Eastern 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. James Wright gets a single email: “You have 8 new forms assigned” with a summary table and deep links to each record, plus “You also have 3 other open forms pending completion.” 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 ([email protected]) using SharedMailboxSendEmailV2, not SendEmailV2. Microsoft recommends shared senders over personal accounts for formalized notifications. The recipient sees “Meridian Performance Management” 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.
NTF-EMAIL-05 and NTF-EMAIL-06 are the only event-driven flows in the notification layer. NTF-EMAIL-05 notifies the author that their form was rejected. NTF-EMAIL-06 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
Meridian has 14 notification flows, not one monolithic flow with 14 branches.
Each flow sends exactly one type of email. NTF-EMAIL-01 sends “Form Assigned” digests. NTF-EMAIL-02 sends “Ready for Signature” digests. NTF-EMAIL-05 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, I check NTF-EMAIL-02. I do not scroll 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 me the problem domain before I open it.
- Management. Sarah Chen wants to disable “Signer Heads-Up” notifications during a pilot? I turn off NTF-EMAIL-04. Nothing else changes. No risk of a condition block accidentally suppressing a different email type.
- Naming. The tag-based naming convention makes every flow discoverable:
Meridian | [NTF-EMAIL-04] Signer Heads-Up - Daily Digest. The tag tells you what it does before you open it.
14 small flows are easier to build, test, monitor, and maintain than 1 large flow with 14 branches. This is not theoretical. I have debugged both patterns. 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 I already notified about?
V1: 24-Hour Lookback (Simple, Fragile)
The initial 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 launch, this was acceptable. The system has monitoring. If a flow fails, we know within hours. But it is not resilient.
V2: Boolean Tracking Flags (Idempotent, Resilient)
The planned improvement adds boolean fields to each notifiable table:
| Table | Field | Purpose |
|---|---|---|
mrd_personnelevaluation | mrd_notified_assigned | Author notified of assignment |
mrd_personnelevaluation | mrd_notified_complete | Author notified of completion |
mrd_evaluationsigningstep | mrd_notified_awaiting | Signer notified of activation |
mrd_evaluationsigningstep | mrd_notified_created | Signer notified of assignment |
mrd_personnelreviewcycle | mrd_notified_launched | Participants notified of launch |
mrd_personnelreviewcycle | mrd_notified_closed | Participants 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.
The 14-Flow Inventory
Here is the complete notification layer. Every flow, its trigger, its recipient, and its priority tier.
Scheduled Flows (Daily 8:00 AM Eastern, Weekdays)
| Tag | Flow Name | Table Queried | Recipient | Subject | Priority |
|---|---|---|---|---|---|
| NTF-EMAIL-01 | Form Assigned | mrd_personnelevaluation | Author | Action Required: Form Name Assigned | P1 |
| NTF-EMAIL-02 | Ready for Signature | mrd_evaluationsigningstep | Signer (non-self) | Action Required: Sign Form Name | P1 |
| NTF-EMAIL-03 | Ready for Acknowledgment | mrd_evaluationsigningstep | Employee (self-signer) | Action Required: Sign Form Name | P1 |
| NTF-EMAIL-04 | Signer Heads-Up | mrd_evaluationsigningstep | Signer (future) | Signature Requested: Form Name | P2 |
| NTF-EMAIL-07 | Evaluation Complete | mrd_personnelevaluation | Author | Complete: All Signatures Received | P1 |
| NTF-EMAIL-08 | Cycle Launched | mrd_personnelreviewcycle | All participants | Review Cycle Has Launched: Cycle Name | P3 |
| NTF-EMAIL-09 | Cycle Closed | mrd_personnelreviewcycle | All participants | Review Cycle Closed: Cycle Name | P3 |
| NTF-EMAIL-10 | Author Reminder | mrd_personnelevaluation | Author | Reminder: Form Name Due | P1 |
| NTF-EMAIL-11 | Author Past Due | mrd_personnelevaluation | Author | Past Due: Form Name | P1 |
| NTF-EMAIL-12 | Supervisor Escalation (Author) | mrd_personnelevaluation | Author’s supervisor | Escalation: Past Due - Author Name | P1 |
| NTF-EMAIL-13 | Signer Past Due | mrd_evaluationsigningstep | Signer | Reminder: Signature Needed | P1 |
| NTF-EMAIL-14 | Supervisor Escalation (Signer) | mrd_evaluationsigningstep | Signer’s supervisor | Escalation: Signature Past Due - Signer Name | P1 |
Real-Time Flows (Event-Driven, Immediate)
| Tag | Flow Name | Trigger | Recipient | Subject | Priority |
|---|---|---|---|---|---|
| NTF-EMAIL-05 | Rejection to Author | Step status changed to Rejected | Author | Rejected: Form Name | P1 |
| NTF-EMAIL-06 | Rejection to Previous Signers | Step status changed to Rejected | Previous signers | Rejected: Form Name | P1 |
Priority Tiers
- P1 (11 flows) - Must-have for go-live. Core signing workflow notifications.
- P2 (1 flow) - Important but informational. NTF-EMAIL-04 can follow shortly after go-live.
- P3 (2 flows) - Nice-to-have. Cycle-level broadcasts can be deferred without impacting individual evaluations.
The NTF-EMAIL tag prefix is deliberate. It leaves room for NTF-INAPP (in-app notifications via the Dataverse notification framework) and NTF-TEAMS (Microsoft Teams adaptive cards) when those channels are added. The naming convention is ready before the channels exist.
Embedded vs Separated: The Full Comparison
| 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 | REV01 creates 47 evaluations, each triggers an embedded email. 47 emails to the same supervisor in seconds. | Scheduled digest batches all 47 into one email per recipient at 8:00 AM. |
| 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 costs you the moment you need to debug, deploy, or disable independently. And you will need to.
What Makes This Work
Three environment variables drive all 14 notification flows:
mrd_EnvironmentURL- Base URL for deep links (changes per environment)mrd_MeridianNotificationsMailbox- Shared mailbox address for SharedMailboxSendEmailV2mrd_MeridianAppID- Model-Driven App GUID for record deep links
Every email includes a direct link to the relevant record: {EnvironmentURL}/main.aspx?etn=mrd_personnelevaluation&id={recordId}&pagetype=entityrecord&appid={AppID}&forceUCI=1. The appid parameter opens the record in the correct app. The forceUCI=1 parameter forces 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
I have rebuilt notification systems three times across different organizations. Every time, the first version embedded notifications in business flows. Every time, the rebuild separated them. 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. 14 flows, zero impact on business logic. 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:
- Tag-Based Flow Architecture - How 3-letter prefixes make 24 flows manageable
- Spec-First Development - Why specs should exist before the designer opens
- Notification Architecture - Notifications that cannot break business logic
- FetchXML in Power Automate - When OData $filter is not enough
- Building Solution ZIPs - The undocumented packaging guide
- What AI Gets Wrong - And why human correction is the point
- 14 Flows in 10 Minutes - 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
The 10-Minute Build: How Specs and AI Produced 14 Power Automate Flows
Power Automate flows built by AI in 10 minutes -- but only because two years of governance made specs machine-readable. The full architecture story.
Spec-Driven Power Platform: The Complete Series
7 articles on building Power Automate flows with specs, governance, and AI. From tag-based architecture to solution packaging to honest AI collaboration.
Tag-Based Flow Architecture: REV, EVL, STP, NTF - 24 Flows, Zero Confusion
Entity-area tags turn 24 Power Automate flows into a navigable architecture. A naming system that maps to ADO, source control, and AI agent batches.