---
name: workflow-design
scope: platform
description: "Workflow design principles: boundaries, versioning (major/minor entity assignment), flow control, cross-retailer, cross-workflow, rulesets. From official Fluent docs."
load: on-demand
relevant-skills:
  - fluent-workflow-builder
  - fluent-workflow-analyzer
  - fluent-workflow-deploy
  - fluent-feature-plan
  - fluent-use-case-discover
  - fluent-rule-scaffold
  - fluent-feature-explain
  - fluent-connection-analysis
  - fluent-pre-deploy-check
version: 1.0
last-updated: 2026-03-06
source: https://docs.fluentcommerce.com/essential-knowledge/designing-workflows
---

# Designing Workflows (Platform Reference)

> Source: Fluent Commerce official documentation (5 Sept 2025).

---

## 1. Workflow Boundaries

Fluent follows **Domain Driven Design** with bounded contexts. Domains are loosely coupled via reference links, not direct relationships (e.g., products and inventory are linked by ref, not a direct FK).

**Workflows are defined per root entity + type field.** A workflow encompasses the root entity and its child entities (the aggregate root pattern).

Example: An Inventory Catalogue is the aggregate root. Its workflow includes the lifecycle for child entities: Inventory Position and Inventory Quantity.

See `domain-model.md` § "Workflow Entity Hierarchy" for the full root/sub-entity mapping.

---

## 2. Execution Model

**Single-threaded, sequential execution:**
- Each event triggers its own execution thread
- Rulesets within an execution run **in sequence**
- Rules within each Ruleset run **in sequence**
- **There is NO parallel split** — no concurrent processing within the same execution context

Flow control is implemented by directing events to different Rulesets (not by branching within a Ruleset).

---

## 3. Flow Control (Logical Gates)

Almost all workflows include logical gates — where execution direction depends on conditions.

**Pattern:** A gate Ruleset contains 2 instances of the same rule type:
1. Instance 1: validates condition = TRUE, sends Event A (to "happy path" Ruleset)
2. Instance 2: validates condition = FALSE, sends Event B (to "alternate path" Ruleset)

This is the standard Fluent pattern for if/else branching. There is no built-in conditional construct — you compose it from sequential rules that check-and-send.

---

## 4. Cross-Retailer Communication

In multi-retailer scenarios, workflows can notify or trigger processes in another retailer's workflow.

**Automatic thread separation:** Rubix detects when the source event's retailer ref differs from the produced event's retailer ref, and processes the cross-retailer event in a separate thread.

See: "Notify Another Retailer" in Rule Actions documentation.

---

## 5. Cross-Workflow Communication

Workflows can trigger processes in other workflows by sending events that target a different entity type or subtype.

**Key requirement:** Cross-workflow events are NOT executed immediately within the same execution context. To trigger cross-workflow execution, add a `scheduled` value (typically the current time) to the produced event. This forces the event into its own execution thread.

See: "Notify Another Workflow" in Rule Actions documentation.

---

## 6. Workflow Versioning

Format: `<major>.<minor>` (e.g., `1.0`, `1.3`, `2.0`).

### Entity-to-Workflow Assignment

When an orchestrable entity is created, it is assigned to the **latest active major version** of the matching workflow.

| Scenario | Behavior |
|---|---|
| Workflow `ORDER::CC` at v1.2, new CC order created | Order assigned to **major version 1** |
| Workflow updated to v1.3 | Same order now uses **v1.3** (latest minor of major 1) |
| New major version v2.0 created | Original order stays on **major 1** (still uses v1.3) |
| New CC order created after v2.0 | New order assigned to **major version 2** |

**Only the latest minor version of a major is ever active.** Minor bumps are backward-compatible — all entities on that major version use the new minor immediately.

### When to Use Major vs Minor

| Change type | Version bump | Effect |
|---|---|---|
| Backward-compatible changes | **Minor** (1.2 → 1.3) | All existing + new entities use the update |
| Incompatible structural changes | **Major** (1.x → 2.0) | Only newly created entities use v2; existing stay on v1 |

**Platform behavior:** Fluent no longer auto-bumps major versions. The platform always makes a **minor version bump** on update. Major version bumps are the developer's responsibility.

### Concrete Example: Major Version Boundary in Action

```
Timeline:
  1. Workflow ORDER::CC is at v99.10
  2. Order #A is created → assigned to major 99 (uses v99.10)
  3. Developer makes breaking ruleset changes and uploads v100.0
  4. Order #A continues processing on major 99 (still v99.10)
     — every event on Order #A executes against v99.10, NOT v100.0
  5. Developer patches v99.11 (minor bump on old major)
     → Order #A now uses v99.11 immediately
  6. Order #B is created → assigned to major 100 (uses v100.0)
  7. Order #A still on major 99, Order #B on major 100
     — they coexist with different workflow logic
```

The major version acts as a **hard boundary** protecting in-flight entities. Entities never cross major versions automatically — they stay on the major they were assigned at creation and always use its latest minor.

### Migration Responsibility

If existing entities need to move to a new major version, the developer must handle migration (state changes, attribute updates, webhooks, etc.). The platform does not auto-migrate.

**During development:** Usually minor-only bumps are appropriate — you want all entities (including test entities) to use the latest workflow. Major versions are more relevant post go-live.

### Version Rules for Deployment

- Version must always **increase** — Fluent rejects same or lower
- Cannot "rollback" to a previous version number — upload old content with a **new higher** version
- `fluent module install --include workflows` bumps minor automatically
- REST API / MCP `workflow.upload` requires explicit version in the JSON

---

## 7. Workflow Identification

Workflows are identified by: **`<ENTITY_TYPE>::<SUB_TYPE>`**

Examples: `ORDER::HD` (Home Delivery), `ORDER::CC` (Click & Collect), `FULFILMENT::HD`, `RETURN_ORDER::RETURN`

The entity's `type` field determines which workflow it's assigned to.

---

## 8. Defining Rulesets

- **Name:** Use meaningful, descriptive names — the workflow should be self-describing
- **Trigger states:** Minimize trigger states per Ruleset. Most Rulesets should have exactly 1 trigger state
- **Rules:** One or more rules, executed sequentially within the Ruleset
- **User actions:** Optional UI-triggered actions (buttons) that send events to this Ruleset

---

## 9. Workflow Settings

Three platform settings can be added to a workflow's `settings` block. All are retailer-context.

### `fc.rubix.sdk.webhook.retry.schedule`

Configures webhook retry back-off pattern.

| Value | Behavior |
|---|---|
| _(default / unset)_ | Retry Fallback Formula: immediate → 2s → 4s → 8s |
| `"exponential"` | Exponential back-off: immediate → 1 min → 1 hour → 1 day |

```json
"settings": {
    "fc.rubix.sdk.webhook.retry.schedule": "exponential"
}
```

### `fc.rubix.sdk.webhook.retry.method`

Controls which rule logic handles webhook retries.

| Value | Behavior |
|---|---|
| _(default / unset)_ | All retries use standard `FLUENTRETAIL.foundation.SendWebhook` |
| `"preserve-event-details"` | Retries use custom webhook rule logic instead of default |

```json
"settings": {
    "fc.rubix.sdk.webhook.retry.method": "preserve-event-details"
}
```

### `fc.rubix.sdk.primaryEntityCache`

Improves workflow performance by caching the primary entity, preventing `context.getEvent()` from making excessive GraphQL calls.

| Value | Behavior |
|---|---|
| _(default / unset)_ | `context.getEvent()` may make unnecessary GraphQL calls |
| `"flush-on-mutation"` | Cache is flushed only on mutations — reduces redundant calls |

**Dependency:** Requires ALL state transitions to be managed by the `SetState` rule from the Core Reference Module. If any ruleset changes state without `SetState`, the cache may serve stale data.

```json
"settings": {
    "fc.rubix.sdk.primaryEntityCache": "flush-on-mutation"
}
```

---

## Quick Reference for Skill Authors

| Question | Answer |
|---|---|
| Can rules run in parallel within one event? | **No** — strictly sequential, single-threaded |
| How to do if/else branching? | Gate Ruleset with 2 rule instances: check+sendA, check+sendB |
| How to trigger another workflow? | Send event with `scheduled` value (current time) |
| How to trigger another retailer? | Send event with different retailer ref — auto-detected |
| Minor version bump effect? | All entities on that major version use it immediately |
| Major version bump effect? | Only new entities use it; existing stay on prior major |
| Does the platform auto-bump major? | **No** — always minor. Major is developer's choice |
| Can I reuse a version number? | **No** — must always increase |
