Logic Recipe
A Recipe enables citizen developers to associate business logic with entity events in Dynamics 365. It dictates when and how to apply particular logic during an entity's lifecycle—create, update, or delete. Recipes are the bridge between your business logic (Logic Blocks and Flows) and Dynamics 365 entity operations.
The Challenge: Scattered Plugin Logic
In traditional Dynamics 365 development, business logic tied to entity events is implemented through plugins and workflows. This approach has several challenges:
- Technical Barrier: Plugins require C# development skills, putting them out of reach for citizen developers
- Deployment Complexity: Plugin changes require compilation, registration, and deployment
- Scattered Logic: Business rules are spread across multiple plugins, making them hard to understand and maintain
- Limited Visibility: Non-developers can't easily see what logic runs when records are created, updated, or deleted
- Testing Difficulty: Plugins must be tested in context of Dynamics 365, making unit testing challenging
The Solution: Centralized Recipe Configuration
Recipes provide a no-code, centralized way to define what happens when entity records are created, updated, or deleted. Instead of writing plugin code, you configure recipes that:
- Validate data before changes are committed
- Execute logic at precise points in the entity lifecycle
- Orchestrate existing Logic Blocks and Flows
- Control execution synchronously or asynchronously
Recipe Structure
A Recipe is defined for a specific entity and consists of three main event sections:
| Component | Description |
|---|---|
| Name | Unique identifier for the recipe |
| Project | The FlowOn Project this recipe belongs to |
| Entity | The Dynamics 365 entity this recipe responds to (Account, Contact, Opportunity, custom entities, etc.) |
| Create | Logic to execute when a record is created |
| Update | Logic to execute when a record is updated |
| Delete | Logic to execute when a record is deleted |
Event Lifecycle Phases
Each event (Create, Update, Delete) has distinct phases where you can inject logic:
┌─────────────────────────────────────────────────────────────────────────────┐
│ ENTITY EVENT LIFECYCLE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ VALIDATION │ ──► │ PRE-EVENT │ ──► │ DATABASE │ │
│ │ Phase │ │ Phase │ │ OPERATION │ │
│ └─────────────┘ └─────────────┘ └──────────────┘ │
│ │ │ │ │
│ │ │ ▼ │
│ │ │ ┌──────────────┐ │
│ │ │ │ POST-EVENT │ │
│ │ │ │ Phase │ │
│ │ │ └──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Run Logic Blocks Run Flows/BPs Run Flows/BPs │
│ (Validation) (Sync or Async) (Sync or Async) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Phase Details
Validation Phase
The Validation phase runs first, before any database operation occurs. This is where you enforce business rules that must pass before the operation can proceed.
| Property | Description |
|---|---|
| Purpose | Validate data and enforce business rules |
| Executes | Logic Blocks only (Validations, Validation Sets, Decision Tables, etc.) |
| Timing | Before any database changes |
| On Failure | Operation is cancelled; error message returned to user |
| Transaction | Within the database transaction (rollback on failure) |
Use Cases:
- Ensure required fields have valid values
- Validate business rule compliance (credit limits, approval thresholds)
- Check referential integrity across related records
- Enforce data format requirements
Pre-Event Phase
The Pre-Event phase runs after validation passes but before the database operation commits. This allows you to modify data or perform related operations that must complete before the main operation.
| Property | Description |
|---|---|
| Purpose | Execute logic before the database operation |
| Executes | Logic Flows or Business Processes |
| Execution Mode | Synchronous or Asynchronous |
| Timing | After validation, before database commit |
| Transaction | Sync: Within transaction / Async: Separate transaction |
Use Cases:
- Auto-populate calculated fields
- Set default values based on business logic
- Create related records that must exist before the main record
- Log audit information
- Notify systems that need to prepare for the change
Post-Event Phase
The Post-Event phase runs after the database operation has successfully completed. This is for actions that depend on the record existing in its new state.
| Property | Description |
|---|---|
| Purpose | Execute logic after the database operation completes |
| Executes | Logic Flows or Business Processes |
| Execution Mode | Synchronous or Asynchronous |
| Timing | After database commit |
| Transaction | Sync: Extended transaction / Async: Separate transaction |
Use Cases:
- Send notification emails
- Update related records
- Sync data to external systems
- Trigger downstream workflows
- Generate documents
- Update aggregated/rollup data
Synchronous vs. Asynchronous Execution
For Pre-Event and Post-Event phases, you choose whether each Flow or Business Process runs synchronously or asynchronously:
| Mode | Behavior | Best For |
|---|---|---|
| Synchronous | User waits for completion; failures can roll back the transaction | Critical operations that must complete; operations where user needs immediate feedback |
| Asynchronous | Executes in background; user doesn't wait; failures don't affect main operation | Long-running processes; non-critical updates; external system integrations; email notifications |
Execution Order
When an entity event triggers, the Recipe executes in this precise order:
For Create Events:
- Validation Logic Blocks (in order defined) → If any fails, operation cancelled
- Pre-Create Flows/BPs (in order defined) → Sync waits; Async queued
- Database INSERT operation
- Post-Create Flows/BPs (in order defined) → Sync waits; Async queued
For Update Events:
- Validation Logic Blocks (in order defined) → If any fails, operation cancelled
- Pre-Update Flows/BPs (in order defined) → Sync waits; Async queued
- Database UPDATE operation
- Post-Update Flows/BPs (in order defined) → Sync waits; Async queued
For Delete Events:
- Validation Logic Blocks (in order defined) → If any fails, operation cancelled
- Pre-Delete Flows/BPs (in order defined) → Sync waits; Async queued
- Database DELETE operation
- Post-Delete Flows/BPs (in order defined) → Sync waits; Async queued
Data Available in Each Phase
Different data is available depending on the event and phase:
| Event | Phase | Available Data |
|---|---|---|
| Create | Validation | New record values (before save) |
| Create | Pre-Create | New record values (before save) |
| Create | Post-Create | Created record with ID |
| Update | Validation | Changed fields, pre-image (before), post-image (after) |
| Update | Pre-Update | Changed fields, pre-image, post-image |
| Update | Post-Update | Updated record, pre-image |
| Delete | Validation | Record to be deleted (pre-image) |
| Delete | Pre-Delete | Record to be deleted (pre-image) |
| Delete | Post-Delete | Deleted record ID, pre-image (snapshot before deletion) |
Complete Example: Customer Account Recipe
Recipe: CustomerAccountRecipe
Project: CRM Automation
Entity: Account
═══════════════════════════════════════════════════════════════════════
CREATE EVENT
═══════════════════════════════════════════════════════════════════════
VALIDATION:
┌────┬─────────────────────────────┬─────────────────┬─────────────────┐
│ # │ Logic Block │ Type │ Purpose │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 1 │ AccountRequiredFields │ Validation Set │ Ensure name, │
│ │ │ │ email, phone │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 2 │ ValidateEmailFormat │ Validation │ Email must be │
│ │ │ │ valid format │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 3 │ CheckDuplicateAccount │ Formula │ No duplicate │
│ │ │ │ account names │
└────┴─────────────────────────────┴─────────────────┴─────────────────┘
PRE-CREATE:
┌────┬─────────────────────────────┬─────────────────┬─────────────────┐
│ # │ Flow/Business Process │ Execution Mode │ Purpose │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 1 │ SetAccountDefaults │ Synchronous │ Set territory, │
│ │ (Flow) │ │ owner, tier │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 2 │ GenerateAccountNumber │ Synchronous │ Auto-generate │
│ │ (Flow) │ │ unique number │
└────┴─────────────────────────────┴─────────────────┴─────────────────┘
POST-CREATE:
┌────┬─────────────────────────────┬─────────────────┬─────────────────┐
│ # │ Flow/Business Process │ Execution Mode │ Purpose │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 1 │ SendWelcomeEmail │ Asynchronous │ Welcome email │
│ │ (Flow) │ │ to new customer │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 2 │ CreateDefaultContacts │ Asynchronous │ Create primary │
│ │ (Flow) │ │ contact record │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 3 │ SyncToSalesforce │ Asynchronous │ Replicate to │
│ │ (Flow) │ │ external CRM │
└────┴─────────────────────────────┴─────────────────┴─────────────────┘
═══════════════════════════════════════════════════════════════════════
UPDATE EVENT
═══════════════════════════════════════════════════════════════════════
VALIDATION:
┌────┬─────────────────────────────┬─────────────────┬─────────────────┐
│ # │ Logic Block │ Type │ Purpose │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 1 │ ValidateStatusChange │ Decision Table │ Only allowed │
│ │ │ │ status changes │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 2 │ ValidateCreditLimit │ Validation │ Credit limit │
│ │ │ │ within range │
└────┴─────────────────────────────┴─────────────────┴─────────────────┘
PRE-UPDATE:
┌────┬─────────────────────────────┬─────────────────┬─────────────────┐
│ # │ Flow/Business Process │ Execution Mode │ Purpose │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 1 │ RecalculateAccountTier │ Synchronous │ Update tier │
│ │ (Flow) │ │ based on revenue│
└────┴─────────────────────────────┴─────────────────┴─────────────────┘
POST-UPDATE:
┌────┬─────────────────────────────┬─────────────────┬─────────────────┐
│ # │ Flow/Business Process │ Execution Mode │ Purpose │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 1 │ NotifyAccountManager │ Asynchronous │ Email on major │
│ │ (Flow) │ │ field changes │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 2 │ UpdateRelatedOpportunities │ Synchronous │ Cascade changes │
│ │ (Flow) │ │ to opportunities│
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 3 │ SyncChangesToSalesforce │ Asynchronous │ Replicate │
│ │ (Flow) │ │ changes external│
└────┴─────────────────────────────┴─────────────────┴─────────────────┘
═══════════════════════════════════════════════════════════════════════
DELETE EVENT
═══════════════════════════════════════════════════════════════════════
VALIDATION:
┌────┬─────────────────────────────┬─────────────────┬─────────────────┐
│ # │ Logic Block │ Type │ Purpose │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 1 │ CanDeleteAccount │ Validation │ No open orders │
│ │ │ │ or invoices │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 2 │ CheckActiveContracts │ Validation │ No active │
│ │ │ │ contracts │
└────┴─────────────────────────────┴─────────────────┴─────────────────┘
PRE-DELETE:
┌────┬─────────────────────────────┬─────────────────┬─────────────────┐
│ # │ Flow/Business Process │ Execution Mode │ Purpose │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 1 │ ArchiveAccountHistory │ Synchronous │ Save audit │
│ │ (Flow) │ │ trail before │
│ │ │ │ deletion │
└────┴─────────────────────────────┴─────────────────┴─────────────────┘
POST-DELETE:
┌────┬─────────────────────────────┬─────────────────┬─────────────────┐
│ # │ Flow/Business Process │ Execution Mode │ Purpose │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 1 │ DeleteFromSalesforce │ Asynchronous │ Remove from │
│ │ (Flow) │ │ external CRM │
├────┼─────────────────────────────┼─────────────────┼─────────────────┤
│ 2 │ NotifyAccountDeletion │ Asynchronous │ Alert relevant │
│ │ (Flow) │ │ stakeholders │
└────┴─────────────────────────────┴─────────────────┴─────────────────┘
Best Practices
Keep Validation Logic Fast: Validation runs synchronously and blocks the user. Use efficient Logic Blocks that execute quickly. Complex validations that query many records should be optimized.
Use Async for Non-Critical Operations: Email notifications, external system syncs, and logging don't need to block the user. Run them asynchronously.
Use Sync for Data Integrity: Operations that must complete for data consistency (calculating totals, creating required related records) should run synchronously.
Order Matters: Logic Blocks and Flows execute in the order defined. Place dependencies before dependents.
Handle Async Failures Gracefully: Asynchronous operations can fail without affecting the main operation. Implement error handling and retry logic in your Flows.
One Recipe Per Entity: Define one comprehensive Recipe per entity rather than multiple overlapping recipes. This keeps logic centralized and predictable.
Test Validation Thoroughly: Validation failures are visible to users. Ensure error messages are clear and helpful. Test edge cases.
Consider Performance: Many synchronous operations slow down the user experience. Balance between data integrity needs and user experience.
Document Your Recipe: Use descriptions to document why each Logic Block and Flow is included. Future maintainers will thank you.