Partie 6 — Dépannage et récapitulatif
Pourquoi cette étape : construire l'application est une chose ; comprendre ce qui casse et pourquoi, c'est ce qui vous rend productif. Cette partie rassemble les erreurs que vous rencontrerez le plus, comment les déboguer, et un récapitulatif de l'ensemble du système.
Erreurs courantes
Les exceptions AUSUS sont nommées précisément — le nom de classe est le diagnostic. Voici celles qu'un débutant rencontre le plus souvent.
FieldRequired
FieldRequired: helpdesk.ticket.title not in payload and no default
Un create a été invoqué sans valeur pour un champ non nullable et sans
défaut. Correction : inclure tout champ requis dans inputs, ou donner
au champ un ->default(...) ou ->nullable(). C'est aussi pour cela que le
bouton Create du moteur ne fonctionne pas en v0.1.0 — voir
la limitation ci-dessous.
PolicyDenied
PolicyDenied: helpdesk.ticket.start
L'acteur ne possède pas le rôle requis par l'action (ticket.agent).
En CLI : passez le rôle dans Application::create(['roles' => [...]]).
En HTTP : envoyez l'en-tête X-Actor-Roles. Si vous l'omettez, le
Router v0.1.0 utilise un jeu de rôles de démonstration qui n'inclut pas
ticket.agent.
WorkflowStateMismatch
WorkflowStateMismatch: current state 'OPEN' does not match any declared source
Vous avez invoqué une transition qui n'est pas légale depuis l'état actuel
de l'enregistrement — par exemple resolve sur un ticket OPEN.
C'est la garde de workflow qui fait son travail. Corrigez l'ordre des
appels, ou ajoutez la transition attendue.
BadRequest: missing required X-Tenant-ID header
Une requête projections/* ou actions/* est arrivée sans X-Tenant-ID.
Correction : envoyer -H 'X-Tenant-ID: helpdesk'. Le moteur React
l'envoie pour vous depuis la prop tenant d'AususProvider.
TenantBoundaryViolation
La référence subject nomme un tenant différent de celui de la requête.
Correction : le tenantId d'une référence sujet doit correspondre à
X-Tenant-ID (helpdesk dans tout ce tutoriel).
AmbiguousWorkflowField
AmbiguousWorkflowField: entity 'helpdesk.ticket' has multiple enum fields
with a default (priority, status)
L'entité a deux champs enum avec des valeurs par défaut et aucun appel
à ->workflow() ; AUSUS ne peut donc pas deviner lequel est le cycle de
vie. Correction : déclarez le workflow explicitement —
->workflow(field: 'status', initial: 'OPEN') — comme le fait ce tutoriel.
(Si vous supprimiez cette ligne, c'est l'erreur que vous verriez.)
ProjectionNotFound / ActionNotFound
Un 404 avec l'un de ces kind signifie que le FQN dans l'URL est faux.
Les FQN sont sensibles à la casse et entièrement qualifiés : c'est
helpdesk.ticket.summary, pas ticket.summary ni summary.
Classe introuvable
Error: Class "Helpdesk\TicketSystem" not found
L'autoloader Composer n'a pas indexé la classe. Correction : vérifiez
que le fichier est src/TicketSystem.php, que le namespace est Helpdesk,
que la règle PSR-4 est dans composer.json, puis lancez
composer dump-autoload.
SQLite ne peut pas ouvrir la base
unable to open database file signifie que le dossier du chemin
database n'existe pas — SQLite crée des fichiers, pas des dossiers.
Correction : utiliser un chemin dont le dossier existe (ce tutoriel
garde tickets.sqlite à la racine du projet).
Conseils de débogage
Une courte liste quand quelque chose ne va pas :
- Lisez la classe d'exception, pas seulement le message.
WorkflowStateMismatch,PolicyDenied,ConcurrencyConflictpointent chacune vers une couche précise. - Isolez en CLI. Si le navigateur se comporte mal, reproduisez en
petit script PHP avec
$app->invoke(...). Cela retire HTTP et React de l'équation et vous dit si le bug est dans le domaine. - Inspectez la ViewSchema directement. Avant d'incriminer le moteur,
lancez
curl -H 'X-Tenant-ID: helpdesk' …/projections/helpdesk.ticket.summaryet lisez le JSON. Si la donnée est fausse là, le problème est côté serveur. - Vérifiez le hash du graphe.
GET /api/_healthrenvoiegraphHash. S'il ne change pas après modification du plugin, le serveur tourne sur du code obsolète — redémarrezphp -S. - Regardez la base de données.
sqlite3 tickets.sqlite 'SELECT * FROM helpdesk_ticket;'affiche les lignes réelles.SELECT action_fqn, timestamp FROM kernel_audit_log;montre chaque action exécutée — la piste d'audit est votre journal d'événements. - Surveillez les notices de dépréciation. Une ligne contenant
AUSUS deprecation:auboot()signifie que vous reposez sur un fallback historique (par ex. un workflow implicite). Ça marche encore, mais corrigez-le avant que cela devienne une erreur. - Lisez le terminal du serveur.
php -Saffiche les erreurs PHP et les piles d'appel dans le terminal où il tourne — gardez cette fenêtre visible.
Récapitulatif de l'architecture
Vous avez écrit un seul fichier de code domaine —
src/TicketSystem.php — et obtenu une application persistée, exposée en
HTTP et rendue dans le navigateur. Voici pourquoi cela fonctionne.
Les couches
TicketSystem plugin ← vous l'avez écrit
│ compilé par le Compiler
▼
MetadataGraph ← entités, champs, actions, workflows, projections
│ consommé par
▼
Runtime / Invoker ← preflight → policy → workflow guard → effect → audit
│ lit et écrit
▼
Persistance SQLite ← schéma dérivé du graphe ; + kernel_audit_log
Et la moitié orientée extérieur :
HTTP Router (ausus/api-http) ← mappe /projections + /actions sur le runtime
│ émet
▼
ViewSchema JSON ← champs + actions + données
│ consommé par
▼
@ausus/renderer-react ← dessine listes, vues de détail, boîtes d'action
Ce qui se passe sur une action
Quand vous avez cliqué sur Start dans le navigateur, voici le trajet complet :
- Le moteur a
POSTé/api/actions/helpdesk.ticket.startavec la référence du ticket et les en-têtes tenant/rôles. - Le
Routerl'a parsé et a appelé l'Invoker. - L'
Invokera exécuté sa chaîne dans une transaction unique : preflight (l'action existe, le sujet est dans le tenant) → policy (l'acteur possèdeticket.agent) → workflow guard (le ticket estOPEN, doncOPEN → IN_PROGRESSest légal) → effect (écrirestatus = IN_PROGRESS) → audit (entrée danskernel_audit_log). - La réponse est repartie en JSON ; le moteur a re-fetché la projection et redessiné la ligne.
Chaque garantie — tenancy, autorisation, vérification de transition légale, entrée d'audit — provient des métadonnées que vous avez déclarées, pas du code écrit par route.
Ce que vous avez construit
- Un plugin de domaine : 1 entité, 5 champs, 4 actions, 1 workflow, 2 projections.
- Une application SQLite exécutée en CLI.
- Une API HTTP avec les routes health, projection et action.
- Une UI React qui liste les tickets et pilote leur cycle de vie.
Limites de documentation restantes
Notes honnêtes sur ce que ce tutoriel n'a pas pu montrer, parce que la capacité n'existe pas en v0.1.x :
- Créer des enregistrements depuis l'UI. Les descripteurs d'action de
ViewSchema ne portent pas encore les métadonnées des champs d'entrée ; le
moteur ne peut donc pas dessiner un formulaire de création. La création
se fait en PHP ou en
curlici. - Authentification. La v0.1.0 n'a pas de couche d'auth ; les rôles
passent en en-têtes / config. Un déploiement réel a besoin d'un
middleware d'auth devant le
Router. - Bases autres que SQLite. La persistance est validée sur SQLite uniquement.
- Filtrage et pagination de liste. Les projections retournent toutes
les lignes du tenant ;
pagination.nextCursorest toujoursnull. - Politiques de garde par transition. L'autorisation est par action ; une politique de garde attachée à une transition individuelle est conçue mais pas exécutée en v0.1.0.
- Entités multiples et références croisées. Ce tutoriel utilise une seule entité ; les relations entre entités ne sont pas couvertes.
Où aller ensuite
- Concepts fondamentaux — le modèle derrière tout ce que vous venez d'utiliser.
- Le DSL PHP — chaque méthode du builder.
- Workflows — transitions, gardes et l'API de workflow explicite en profondeur.
- L'API HTTP — la référence complète des routes et erreurs.
Vous avez construit une application AUSUS complète depuis zéro. Tout ce qui est plus grand n'est que les quatre mêmes mouvements : décrire le domaine, amorcer l'Application, l'exposer, l'afficher.