Aller au contenu principal

Architecture Workflows – État technique et propositions

Dernière mise à jour : 2026-08-24

Ce document est la source de vérité de la brique Workflows (post v2) : modèle, orchestration, écarts doc/code, et propositions.

Voir aussi : WORKFLOW_TRIGGERS.md · WORKFLOW_VISUAL_EDITOR.md · QUEUE_SYSTEM.md · SESSION_INFO_RENSEIGNEMENTS.md


0. Vue d’ensemble​

Un workflow est un playbook d’automatisation CRM : phases → tâches → actions, rattaché à une session. Exécution NestJS + BullMQ (WORKFLOW_TASKS). Ce n’est pas n8n ; Iris peut seulement passer un workflow_id à la création/màj de session.

flowchart LR
subgraph Design
T[Templates système]
U[Workflows user]
P[Phases / Tâches / Actions]
TR[Triggers]
T -->|fork| U
U --> P
U --> TR
end

subgraph Bind
ST[Session types<br/>default_workflow_id]
S[sessions.workflow_id]
ST --> S
U --> S
end

subgraph Run
I[session_workflow_instances]
R[session_workflow_task_runs]
Q[BullMQ workflow-tasks]
E[ExecutionService]
S --> I --> R --> Q --> E
end

TR -->|événements SESSION| R
CoucheRôle
DesignTemplates admin → fork user → édition phases / tâches / actions / triggers
BindType de séance ou apply-workflow → sessions.workflow_id
Run1 instance / session → task runs planifiés → cron 5 min → queue → exécution

1. Modèle de données (post-migration 0173)​

1.1 Relations (ER)​

erDiagram
workflows ||--o{ workflow_phases : contains
workflow_phases ||--o{ workflow_tasks : contains
workflow_tasks ||--o{ workflow_task_actions : has
workflows ||--o{ workflow_triggers : triggers
workflow_triggers ||--o{ workflow_trigger_executions : logs
workflows ||--o{ workflows : "forked_from_id"
workflows ||--o| sessions : "sessions.workflow_id"
sessions ||--o| session_workflow_instances : "1:1"
session_workflow_instances ||--o{ session_workflow_task_runs : runs
session_workflow_task_runs ||--o{ session_workflow_action_runs : "schema only"
user_session_types }o--o| workflows : default_workflow_id
lead_forms }o--o| workflows : workflow_id

1.2 Tables principales​

TableRôle
workflowsDéfinitions unifiées : templates (is_template=true, owner_id=null) + copies user (owner_id, forked_from_id). is_published, display_order.
workflow_phasesPhases ordonnées. phase_type : CREATION, CONFIRMATION, DATE, CUSTOM.
workflow_tasksTâches : title, relative_days + relative_unit (HOURS/DAYS/WEEKS/MONTHS), is_manual. Pas de colonne task_type / action — dérivés des actions.
workflow_task_actions0–N actions / tâche. Types : SEND_EMAIL, SEND_SMS, CLIENT_PORTAL_INVITE, SEND_NOTIFICATION, CREATE_REMINDER, WEBHOOK, ASSIGN_FORM, CHANGE_SESSION_STATUS, SEND_SESSION_INFO_INVITE, SEND_SURVEY_INVITE. Config JSONB (email_template_id, sms_template_id, action ex. SESSION_NOTIFICATION:appointment-reminder-1w, survey_template_id, …).
workflow_triggerstrigger_event, conditions JSONB, schedule_config, is_active, execution_order.
workflow_trigger_executionsAudit trigger : PENDING / EXECUTED / FAILED / SKIPPED. Unique (trigger_id, entity_type, entity_id).
session_workflow_instances1 instance / session (UNIQUE(session_id)). Statut : ACTIVE / PAUSED / COMPLETED / CANCELLED.
session_workflow_task_runsRuns : PENDING / SCHEDULED / COMPLETED / SKIPPED / FAILED + scheduled_at / executed_at / metadata.
session_workflow_action_runsPrévu pour l’audit par action — schéma présent, non écrit par le runtime actuel.

1.3 Tables legacy supprimées​

Supprimées en 0173 : user_workflows, user_workflow_phases, user_workflow_tasks, default_workflows, default_workflow_phases, default_workflow_tasks, session_workflow_tasks.

Ne plus les référencer dans le runtime. Reliquats éventuels en base = dettes de schéma, pas de dépendances applicatives.

1.4 Conditions des triggers​

Structure attendue par le code (pas d’imbrication de groupes) :

{
"operator": "AND",
"conditions": [
{ "field": "status", "operator": "equals", "value": "VALIDATED" }
]
}

Détail opérateurs / champs : WORKFLOW_TRIGGERS.md. Pour FORM_SUBMITTED / SURVEY_SUBMITTED, les réponses sont exposées dans answers.<fieldId> et answers.<label_slug>.


2. Orchestration​

2.1 Flux bout-en-bout​

flowchart TD
A[Événement domaine] --> B[evaluateTriggersForEvent]
B --> C{Triggers actifs<br/>owner + event}
C -->|aucun| Z[Stop]
C -->|chaque trigger| D{Déjà EXECUTED?}
D -->|oui| Z2[Skip]
D -->|non| E[ConditionEvaluator]
E -->|fail| F[Record SKIPPED]
E -->|pass| G[resolveSessionId]
G -->|CONTACT / FORM_SUBMISSION<br/>sans session| H[Record SKIPPED]
G -->|session sans workflow_id| H
G -->|session + workflow_id| I[createWorkflowTasksForSession + reminders]
I --> L[Record trigger EXECUTED]

M[Cron toutes les 5 min] --> N[getTasksReadyForExecution]
N --> O[BullMQ WORKFLOW_TASKS]
O --> P[WorkflowTasksProcessor]
P --> Q[WorkflowTasksExecutionService.executeTask]
Q --> R{Type dérivé des actions}
R -->|TASK| S[mark COMPLETED]
R -->|AUTOMATIC_EMAIL| T[email / portal / session-info / survey / notification]
R -->|AUTOMATIC_SMS| U[SmsService]
T --> V[COMPLETED ou FAILED]
U --> V

2.2 Rattachement session (sans trigger)​

sequenceDiagram
participant User
participant UI as Session form / Tasks
participant API as Sessions API
participant Create as WorkflowTasksCreationService
participant DB

User->>UI: Choisit type de séance
UI->>UI: Préremplit workflow_id<br/>depuis default_workflow_id
User->>API: Create / update session
API->>Create: createWorkflowTasksForSession<br/>(onlyCreationPhase si create)
Create->>DB: Upsert instance + task runs CREATION
Note over API,DB: Statut → VALIDATED
API->>Create: recreate CONFIRMATION + DATE
Create->>DB: Delete/recreate runs phases concernées

Chemins d’attache :

  1. Type de séance → user_session_types.default_workflow_id → sessions.workflow_id
  2. Choix manuel / onglet Tasks / PUT /sessions/:id/apply-workflow
  3. Bootstrap user : fork templates + relink session types (createDefaultWorkflowsForUser)
  4. Lead form → lead_forms.workflow_id passé à sessions.create si la soumission crée une session (champ DATE)

2.3 Déclenchement (triggers)​

EventCallerEntityExécution réelle ?
SESSION_CREATEDSessionIntegrationServiceSESSIONOui*
SESSION_STATUS_CHANGEDidem (y compris remap depuis SESSION_UPDATED)SESSIONOui*
SESSION_INFO_SUBMITTEDSessionClientInfoServiceSESSIONOui*
SURVEY_SUBMITTEDSurveySubmittedWorkflowTriggerHandler (@OnEvent)SESSIONOui* (entityData.answers)
QUOTE_STATUS_CHANGEDQuotes CRUDQUOTEOui* (via session_id)
INVOICE_STATUS_CHANGED / PAYMENT_RECEIVEDInvoices CRUDINVOICEOui* (via session_id)
FORM_SUBMITTEDFormSubmittedWorkflowTriggerHandler (@OnEvent lead-form.submitted)SESSION si session créée, sinon FORM_SUBMISSIONOui* si session + workflow_id ; sinon SKIPPED
CONTACT_CREATEDContactsServiceCONTACTSKIPPED
SCHEDULED——Non évalué

*Oui = si conditions OK → résoudre la session liée → createWorkflowTasksForSession + createNotificationTasksForSession (nécessite sessions.workflow_id). Les triggers ne positionnent pas session.workflow_id = trigger.workflow_id. Exception rattachement : lead_forms.workflow_id est passé à sessions.create (hors moteur trigger).

flowchart LR
subgraph Actifs
SC[SESSION_CREATED]
SS[SESSION_STATUS_CHANGED]
SI[SESSION_INFO_SUBMITTED]
SU[SURVEY_SUBMITTED]
FS[FORM_SUBMITTED]
QS[QUOTE_STATUS_CHANGED]
IS[INVOICE_STATUS_CHANGED]
PR[PAYMENT_RECEIVED]
end

subgraph Audit_only["Évalués puis SKIPPED"]
CC[CONTACT_CREATED]
FS0[FORM_SUBMITTED sans session]
end

subgraph Absent
SCH[SCHEDULED]
end

Actifs -->|SESSION id ou session_id lié| Run[createWorkflowTasksForSession + reminders]
Audit_only --> Log[trigger_executions SKIPPED]
Absent --> X[Pas d'appel]

2.4 Création des task runs​

  • Service : WorkflowTasksCreationService.createWorkflowTasksForSession(sessionId)
  • Upsert session_workflow_instances, puis insert des session_workflow_task_runs depuis les workflow_tasks
  • Filtre phases :
    • toujours : CREATION (+ CUSTOM)
    • CONFIRMATION / DATE : seulement si session VALIDATED (sauf onlyCreationPhase)
  • createNotificationTasksForSession recrée des session_workflow_task_runs dédiés (task_id null, metadata.kind = session_reminder, action = SESSION_NOTIFICATION:<id>) depuis user_metadata.session_reminder_settings
  • scheduled_at = session.start_date − hours_before ; runs passés ignorés
  • Nécessite sessions.workflow_id (instance FK) ; sans workflow, skip
  • Les runs rappel ne sont pas effacés par le recreate des phases workflow (phase_id null)

Date de référence pour scheduled_at :

PhaseRéférence
CREATIONsession.created_at
DATEsession.start_date (sinon now)
CONFIRMATION / CUSTOMnow (à la création des runs)

Puis : scheduled_at = référence ± relative_days / relative_unit.

2.5 Exécution des tâches (scheduler + queue)​

sequenceDiagram
participant Cron as WorkflowTasksScheduler
participant Q as BullMQ WORKFLOW_TASKS
participant Proc as WorkflowTasksProcessor
participant Exec as WorkflowTasksExecutionService
participant Ext as Email / SMS / Invites

loop Toutes les 5 min
Cron->>Cron: getTasksReadyForExecution<br/>PENDING/SCHEDULED + scheduled_at ≤ now
Cron->>Q: addWorkflowTaskJob
end
Q->>Proc: consume job
Proc->>Exec: executeTask
Exec->>Exec: derive task_type from first action
alt TASK (is_manual)
Exec->>Exec: mark COMPLETED
else AUTOMATIC_EMAIL
Exec->>Ext: email / portal / session-info / survey / SESSION_NOTIFICATION
else AUTOMATIC_SMS
Exec->>Ext: SmsService
end

Dérivation runtime (WorkflowTasksQueryService.toExecutionTask) :

  • is_manual → TASK
  • première action SEND_SMS → AUTOMATIC_SMS
  • actions email-like → AUTOMATIC_EMAIL
  • actions spéciales : CLIENT_PORTAL_INVITE, SEND_SESSION_INFO_INVITE, SEND_SURVEY_INVITE, ou config.action (SESSION_NOTIFICATION:…)

Note : les tâches « manuelles » sont quand même pickées par le cron à échéance et marquées COMPLETED (comportement checklist due-date, pas « attendre l’utilisateur »).

2.6 Cycle de vie (statuts)​

stateDiagram-v2
[*] --> ACTIVE: instance créée / upsert
ACTIVE --> PAUSED
ACTIVE --> COMPLETED
ACTIVE --> CANCELLED: workflow retiré / runs deleted

state task_run {
[*] --> PENDING: scheduled_at calculé
PENDING --> SCHEDULED
PENDING --> COMPLETED: cron + queue
SCHEDULED --> COMPLETED
PENDING --> FAILED
SCHEDULED --> FAILED
PENDING --> SKIPPED
}

3. Surfaces API et frontend​

flowchart TB
subgraph Backend
WC[WorkflowsController]
WA[WorkflowsAdminController]
WT[WorkflowTriggersController]
SA[PUT /sessions/:id/apply-workflow]
Mod[workflows/ + workflow-tasks/]
WC --> Mod
WA --> Mod
WT --> Mod
SA --> Mod
end

subgraph Frontend
WF[WorkflowFormPage — éditeur 2 colonnes]
DW[DefaultWorkflowsTab / Admin templates]
ST[Session TasksTab]
WF --> WC
DW --> WA
ST --> SA
end
  • Workflows : CRUD user ; admin/defaults (templates, bootstrap user)
  • Triggers : CRUD sous /workflows/:workflowId/triggers
  • Session : apply-workflow rattache + recrée les task runs
  • Frontend : éditeur Flow + panneau édition ; pas de dry-run / simulate ; pas de DnD de réordonnancement (décision produit)

4. Écarts documentation / code​

Doc / contratÉcart
WORKFLOW_TRIGGERS.mdQUOTE/INVOICE exécutés via session_id ; FORM_SUBMITTED via session créée ; CONTACT SKIPPED ; SCHEDULED non évalué.
WORKFLOW_VISUAL_EDITOR.mdAligné (2 colonnes, pas de DnD, validation champs obligatoires, sous-tâches 1 niveau).
WORKFLOWS_REVIEW_PLAN.mdSuivre le modèle 0173 (pas de session_workflow_tasks).
SESSION_NOTIFICATIONS (pré-v2)Historique git uniquement — ne plus implémenter dessus.
OpenAPI RelativeUnitAPI : HOURS/DAYS/MONTHS/YEARS. DB : HOURS/DAYS/WEEKS/MONTHS. À aligner.
lead_forms.workflow_idPassé à sessions.create si un champ DATE crée une session ; FORM_SUBMITTED évalue ensuite les réponses. Sans session → SKIPPED.
session_workflow_action_runsTable morte côté runtime.

5. Gotchas (comportement réel)​

  1. Triggers ≠ assignation — ils régénèrent les runs du workflow_id courant de la session (SESSION directe, ou session liée pour QUOTE/INVOICE) ; sans workflow_id / sans session_id, SKIPPED.
  2. CONTACT_CREATED — évalué puis SKIPPED (pas de cible session unique). FORM_SUBMITTED sans session (pas de champ DATE / création échouée) idem.
  3. 1 instance / session — changer de workflow met à jour la même ligne d’instance ; pas de multi-workflow parallèle.
  4. Re-apply / validate — delete + recreate des runs des phases concernées (historique reset) ; rappels session (metadata.kind) sont régénérés à part.
  5. SESSION_UPDATED → remap SESSION_STATUS_CHANGED — dédup via unique (trigger_id, entity_type, entity_id).
  6. Couverture actions inégale — exécution centrée email / SMS / invites ; WEBHOOK, ASSIGN_FORM, CHANGE_SESSION_STATUS, CREATE_REMINDER peu (ou pas) branchés.
  7. Conditions JSON : préférer conditions (UI/DTO/seeds) ; d’anciennes traces rules possibles en migration.

6. Propositions d’amélioration​

6.1 Documentation / contrats​

  1. Garder ce fichier comme source active (modèle v2 + orchestration visuelle).
  2. Aligner WORKFLOW_TRIGGERS.md sur les events réellement exécutés (SESSION + INFO + SURVEY + FORM + QUOTE/INVOICE).
  3. Aligner OpenAPI RelativeUnit avec la DB (WEEKS vs YEARS).

6.2 Fonctionnel (priorité produit : relances)​

  1. Rappels session — ✅ Option A livrée (task runs metadata SESSION_NOTIFICATION:… via createNotificationTasksForSession). Limite restante : session sans workflow_id → skip. Options B/C non retenues pour l’instant.
  2. Triggers devis / factures / paiement — ✅ exécutés via session_id lié (même moteur session). CONTACT_CREATED reste SKIPPED.
  3. SCHEDULED — cron dédié sur schedule_config.
  4. Lead forms — ✅ FORM_SUBMITTED + lead_forms.workflow_id à la soumission (session DATE). Sans session → SKIPPED.

6.3 Observabilité et robustesse​

  1. Vue / endpoint exécutions récentes (trigger_executions + task_runs) par workflow.
  2. Logs / métriques standard : workflowId, triggerId, taskId, sessionId.
  3. Garde-fous validation phases/tâches (API + UI) ; alignement DTO ↔ DB ↔ frontend.

7. Fichiers clés​

RôleEmplacement
Module workflowsbackend/src/workflows/
CRUD / templates / publishworkflow-crud.service.ts, workflow-phases.service.ts, workflow-template.service.ts, workflow-publish-validation.service.ts
Controllersworkflows.controller.ts, workflows-admin.controller.ts, workflow-triggers.controller.ts
Triggersworkflow-triggers.service.ts, helpers/condition-evaluator.helper.ts, survey-submitted-workflow-trigger.handler.ts, form-submitted-workflow-trigger.handler.ts
Runtime tasksbackend/src/workflow-tasks/ — creation, execution, query, helpers, scheduler, facade
Queuebackend/src/queue/processors/workflow-tasks.processor.ts
Session gluesession-integration.service.ts, apply-workflow sur sessions
OpenAPIopenapi/components/workflows.yaml, openapi/paths/workflows*.yaml
FrontendWorkflowsPage / WorkflowFormPage, TasksTab, admin default workflows, useWorkflows / useWorkflowTriggers

8. Références​