API & Intelligence

Backend

Symfony 8 en architecture hexagonale, PostgreSQL multi-tenant, agent IA Claude avec function calling, et communication SMS bidirectionnelle.

01 Architecture Hexagonale

Separation stricte Domain / Application / Infrastructure.

Domain

Entites metier pures (Telemetry, Device, Trip, User), value objects, interfaces de repositories. Aucune dependance framework.

Coeur metier

Application

Use cases / services applicatifs : IngestTelemetry, StartTrip, AskJarvis, SendCommand. Orchestration entre le domaine et les ports.

Use Cases

Infrastructure

Adaptateurs concrets : Doctrine ORM (PostgreSQL), Mercure publisher, Claude API client, SQS queue consumer, SMS gateway. Implementation des ports definis par le domaine.

Adaptateurs

02 API REST (Symfony 8)

Points d'entree principaux de l'application.

MethodeEndpointDescriptionAuth
POST/api/telemetryReception des donnees du module 4G (ingestion via queue en phase scale)X-Device-Token
GET/api/telemetry/{device}/latestDerniere telemetrie connueJWT
GET/api/telemetry/{device}/historyHistorique avec filtres temporelsJWT
GET/api/tripsListe des trajets enregistres (avec trace PostGIS)JWT
POST/api/commandEnvoyer une commande au module (fibre SUNKENET, reboot...)JWT
POST/api/jarvis/chatEnvoyer une question a l'agent IA JarvisJWT
GET/api/jarvis/actionsHistorique des actions executees par JarvisJWT
POST/webhook/smsReception SMS entrant (webhook modem)X-Device-Token
GET/api/fuel/stationsStations essence proches (proxy prix-carburants.gouv.fr)JWT
GET/api/firmware/latestVersion firmware disponible pour OTAX-Device-Token

03 Modele PostgreSQL (Multi-Tenant)

Schema optimise pour les series temporelles vehiculaires avec isolation multi-tenant.

Le modele de donnees repose sur cinq tables principales :

TableRoleParticularites
tenantsIsolation multi-tenantChaque organisation ou utilisateur possede un tenant avec un plan (free, pro, etc.)
usersComptes utilisateursRattaches a un tenant, authentification par email/mot de passe, roles JSONB
devicesModules IoT enregistresChaque device est lie a un tenant et un proprietaire, avec un token d'authentification hache (bcrypt)
telemetryDonnees capteurs en serie temporellePartitionnee par mois, colonne JSONB flexible pour tous les capteurs OBD, index GiST (geographie) et BRIN (temporel)
tripsTrajets enregistresTrace GPS (PostGIS LineString), statistiques agregees (vitesse moy, RPM moy, conso, boost max)

JSONB flexible : La colonne data accepte tous les capteurs OBD (standard + BMW UDS) sans migration de schema. Exemple M57 : {"rpm": 3000, "speed": 120, "coolant_temp": 95, "oil_temp": 105, "rail_pressure": 1350, "dpf_soot": 12.5, "turbo_boost": 1.8}

Multi-tenant : Toutes les requetes Doctrine incluent un filtre tenant_id automatique via un Doctrine Filter global. Un utilisateur ne peut jamais acceder aux donnees d'un autre tenant.

04 Agent IA — Jarvis (Claude)

Agent autonome Claude avec function calling. Remplace OpenAI — licence entreprise MAX.

Architecture Agent

Jarvis n'est pas un simple chatbot. C'est un agent autonome qui dispose d'un knowledge graph sur l'utilisateur (habitudes, lieux frequents, horaires) et de tools qu'il peut invoquer de maniere autonome via le protocole function calling de Claude.

Symfony construit un contexte structure avant chaque requete a Claude, incluant l'etat du vehicule en temps reel (RPM, temperatures, pressions, localisation), les alertes actives, et les habitudes de l'utilisateur (lieux frequents, station essence habituelle, horaires de trajet).

Function Calls — Tableau recapitulatif

ToolParametresEffet
change_fiber_colorcolor: stringChange la couleur du kit fibre optique SUNKENET
search_fuel_stationfuel_type, radius_kmRecherche stations diesel pas cheres (prix-carburants.gouv.fr)
send_smsto, bodyEnvoie un SMS via le modem 4G
send_push_notificationmessage, levelNotification push sur le dashboard React
read_emailcountLit les derniers emails (Gmail/Outlook API)
check_calendardateConsulte le calendrier Google Calendar
calculate_routedestinationCalcul d'itineraire optimise
set_monitoring_freqpid, hzAjuste la frequence de lecture d'un capteur OBD

Securite IA : Toutes les function calls retournees par Claude passent par un validateur Symfony (Application/Validator/ToolCallValidator) avant execution. L'IA ne peut pas envoyer de commandes arbitraires au vehicule. Les tools autorises sont definis par la configuration du tenant.

05 Queue d'Ingestion (SQS / RabbitMQ)

Ingestion asynchrone pour absorber les pics de telemetrie.

1

Reception HTTP

Le controller /api/telemetry valide le token, puis pousse le payload dans la queue SQS au lieu de persister directement.

2

Consumer asynchrone

Un worker Symfony Messenger consomme la queue, persiste en PostgreSQL et publie sur Mercure. Plusieurs workers en parallele sur ECS/Fargate.

3

Analyse et alertes

Apres persistance, un second handler analyse les tendances. Si anomalie detectee (temperature huile M57 trop haute, DPF sature), declenchement d'une alerte via Jarvis.

Phase 1 (MVP) : ingestion synchrone directe en PostgreSQL. La queue SQS sera activee en Phase 4 quand le nombre de devices augmentera significativement.

06 Temps Reel (Mercure)

Publication serveur-vers-client sans polling.

A chaque reception de telemetrie, l'API Symfony valide le X-Device-Token, persiste les donnees en PostgreSQL (RPM, temperature huile, pression common rail, DPF, turbo...), puis publie une mise a jour Mercure sur le topic du device. Le dashboard React recoit instantanement les nouvelles valeurs via Server-Sent Events.

07 Passerelle SMS

Interaction bidirectionnelle par SMS. Reboot module a distance, alertes urgentes.

1

Reception

Le modem 4G recoit un SMS et le transmet a Symfony via POST /webhook/sms.

2

Autorisation

Symfony verifie si le numero expediteur est dans la liste blanche du tenant.

3

Traitement

Si commande systeme (ex: "REBOOT") : execution directe. Sinon : envoi a Jarvis avec le contexte vehiculaire.

4

Reponse

Symfony commande au modem 4G d'envoyer le SMS de reponse. Cas d'usage : alerte station essence pas chere, alerte vehicule, confirmation reboot.

08 Securite

Authentification multi-couche et isolation des donnees.

🔒

Device Token

Chaque module IoT possede un X-Device-Token unique. Le hash bcrypt est stocke en base (table devices). Token en NVS chiffre sur l'ESP32. Validation a chaque requete entrante.

Critique
🔑

JWT Utilisateur

Les endpoints dashboard sont proteges par JWT (LexikJWTAuthenticationBundle). Token renouvele via refresh token. Le JWT contient le tenant_id pour le filtrage automatique.

Auth
🛡

HTTPS + Fingerprint

Le module ESP32 valide l'empreinte du certificat SSL du serveur pour empecher les attaques MITM. Certificat stocke dans la partition data de l'ESP32.

TLS

Validation IA

Toutes les function calls de Claude sont validees par un ToolCallValidator Symfony avant execution. Les tools autorises sont configures par tenant. Logging complet de chaque action IA.

AI Safety

Isolation multi-tenant : Un Doctrine Filter global injecte automatiquement le tenant_id dans chaque requete SQL. Impossible pour un utilisateur d'acceder aux donnees d'un autre tenant, meme en cas de bug applicatif.

Architecture Hexagonale — Detail

Arborescence complete du code backend avec explication de chaque couche et regles de dependance.

src/
├── Domain/                          # Coeur metier — ZERO dependance externe
│   ├── Entity/
│   │   ├── Telemetry.php            # Entite telemetrie (RPM, temp, GPS...)
│   │   ├── Device.php               # Module IoT enregistre
│   │   ├── Trip.php                 # Trajet enregistre
│   │   ├── User.php                 # Utilisateur du dashboard
│   │   └── Tenant.php               # Organisation multi-tenant
│   ├── ValueObject/
│   │   ├── DeviceToken.php          # Value object token device (immutable)
│   │   ├── GpsCoordinate.php        # Latitude + longitude
│   │   ├── TelemetryData.php        # Donnees capteurs typees
│   │   └── FiberColor.php           # Couleur RGBW fibre SUNKENET
│   ├── Port/
│   │   ├── TelemetryRepositoryInterface.php
│   │   ├── DeviceRepositoryInterface.php
│   │   ├── TripRepositoryInterface.php
│   │   ├── AiAgentInterface.php     # Port vers Claude API
│   │   ├── RealtimePublisherInterface.php  # Port vers Mercure
│   │   └── SmsGatewayInterface.php  # Port vers passerelle SMS
│   └── Exception/
│       ├── DeviceNotFoundException.php
│       └── UnauthorizedDeviceException.php
│
├── Application/                     # Use cases — depend UNIQUEMENT de Domain
│   ├── UseCase/
│   │   ├── IngestTelemetry.php      # Reception + persistance + publish Mercure
│   │   ├── StartTrip.php            # Demarrage d'un nouveau trajet
│   │   ├── EndTrip.php              # Fin de trajet + calcul stats
│   │   ├── AskJarvis.php            # Envoi question a l'agent IA
│   │   ├── SendDeviceCommand.php    # Envoi commande vers module IoT
│   │   └── ProcessIncomingSms.php   # Traitement SMS entrant
│   ├── DTO/
│   │   ├── TelemetryPayload.php     # DTO ingestion telemetrie
│   │   ├── JarvisRequest.php        # DTO requete Jarvis
│   │   └── DeviceCommandPayload.php # DTO commande device
│   └── Validator/
│       └── ToolCallValidator.php    # Validation des function calls IA
│
└── Infrastructure/                  # Adaptateurs — depend de Domain + libs externes
    ├── Persistence/
    │   ├── DoctrineTelemetryRepository.php
    │   ├── DoctrineDeviceRepository.php
    │   └── DoctrineTripRepository.php
    ├── Messaging/
    │   ├── MercureRealtimePublisher.php
    │   └── SqsTelemetryConsumer.php
    ├── Ai/
    │   └── ClaudeAgentAdapter.php   # Client Claude API + function calling
    ├── Sms/
    │   └── ModemSmsGateway.php      # Envoi/reception SMS via modem 4G
    └── Http/
        ├── Controller/
        │   ├── TelemetryController.php
        │   ├── JarvisController.php
        │   ├── CommandController.php
        │   └── SmsWebhookController.php
        └── Security/
            ├── DeviceTokenAuthenticator.php
            └── TenantFilter.php     # Doctrine Filter multi-tenant

Regle de dependance : Les fleches de dependance vont toujours vers l'interieur. Infrastructure depend de Application et Domain. Application depend uniquement de Domain. Domain ne depend de rien d'externe. Cette regle est verifiee par PHPStan + Deptrac.

Swagger / OpenAPI

Documentation interactive de l'API generee par NelmioApiDocBundle.

Acces : Swagger UI est disponible a http://localhost:8080/api/doc. Le schema OpenAPI 3.0 JSON est a /api/doc.json.

Configuration NelmioApiDocBundle

  • Fichier : config/packages/nelmio_api_doc.yaml
  • Annotations : Utiliser les attributs PHP 8 #[OA\Tag], #[OA\Response], #[OA\RequestBody], #[OA\Parameter] sur chaque methode de controller
  • Groupes : Les endpoints sont groupes par tag (Telemetry, Jarvis, Device, Auth)
  • Securite : Les schemas d'authentification JWT (Bearer) et X-Device-Token (ApiKey) sont documentes dans la configuration globale

Ajouter un nouvel endpoint

  1. Ajouter les attributs OpenAPI sur la methode du controller
  2. Definir le DTO de requete/reponse avec les attributs #[OA\Property]
  3. Rafraichir Swagger UI — la doc est generee automatiquement

Migrations Doctrine

Gestion du schema de base de donnees via les migrations Doctrine.

CommandeDescription
php bin/console doctrine:migrations:diffGenere une migration a partir des differences entre les entites et le schema actuel
php bin/console doctrine:migrations:migrateExecute toutes les migrations en attente
php bin/console doctrine:migrations:statusAffiche l'etat des migrations (executees, en attente)
php bin/console doctrine:schema:validateVerifie la coherence entre les entites et le schema SQL
php bin/console doctrine:database:createCree la base de donnees si elle n'existe pas
php bin/console doctrine:fixtures:loadCharge les fixtures (donnees de test) — attention : ecrase les donnees existantes

Partitionnement : La table telemetry est partitionnee par mois. Les migrations Doctrine ne gerent pas nativement le partitionnement PostgreSQL — les migrations de partitionnement sont ecrites en SQL brut dans des fichiers de migration custom.

Tests (PHPUnit)

Strategie de tests et configuration de l'environnement de test.

TypeRepertoireCible
Unit teststests/Unit/Entites Domain, value objects, use cases Application (mocks des ports)
Integration teststests/Integration/Repositories Doctrine, Mercure publisher, Claude adapter (avec mocks HTTP)
Functional teststests/Functional/Controllers API (WebTestCase) — test du flux complet requete/reponse
CommandeDescription
php bin/phpunitLance tous les tests
php bin/phpunit --filter TelemetryTestLance un test specifique
php bin/phpunit --testsuite unitLance uniquement les tests unitaires
php bin/phpunit --coverage-html var/coverageGenere un rapport de couverture HTML

Base de test : Les tests d'integration et fonctionnels utilisent une base PostgreSQL dediee (app_test) configuree dans .env.test. Les fixtures sont chargees automatiquement avant chaque suite de tests.

Commandes Utiles

Commandes Symfony et Doctrine frequemment utilisees en developpement.

CommandeDescription
php bin/console cache:clearVide le cache Symfony (obligatoire apres changement de config)
php bin/console debug:routerListe toutes les routes enregistrees avec methodes et patterns
php bin/console debug:containerListe tous les services du container avec leurs classes
php bin/console debug:event-dispatcherListe les event listeners enregistres
php bin/console messenger:consume asyncDemarre le worker Messenger pour consommer la queue SQS
php bin/console lexik:jwt:generate-keypairGenere les cles RSA pour LexikJWTAuthenticationBundle
php bin/console make:entityGenere une nouvelle entite Doctrine (interactif)
php bin/console make:controllerGenere un nouveau controller
composer phpstanAnalyse statique PHPStan (niveau 8)
composer cs-fixCorrection automatique du coding standard (PHP-CS-Fixer)

Ajouter une Nouvelle Feature

Guide pas-a-pas pour ajouter une fonctionnalite en suivant l'architecture hexagonale.

1

Domain — Entite / Value Object

Creer l'entite metier dans src/Domain/Entity/. Si besoin, ajouter des value objects dans src/Domain/ValueObject/. L'entite ne doit dependre d'aucune librairie externe (pas de Doctrine annotations ici, uniquement des attributs PHP purs).

2

Domain — Interface du Port

Definir l'interface du repository ou du service externe dans src/Domain/Port/. Ex: AlertRepositoryInterface.php avec les methodes save(), findByDevice(), etc.

3

Application — Use Case

Creer le use case dans src/Application/UseCase/. Il recoit les ports en injection de dependance (constructor). Il orchestre la logique metier sans connaitre les implementations concretes. Creer le DTO dans src/Application/DTO/.

4

Infrastructure — Adaptateur

Implementer le port dans src/Infrastructure/. Ex: DoctrineAlertRepository.php qui implemente AlertRepositoryInterface avec Doctrine ORM. Ajouter le mapping Doctrine (XML ou attributs).

5

Infrastructure — Controller

Creer le controller HTTP dans src/Infrastructure/Http/Controller/. Il recoit le use case en injection, valide le DTO, appelle le use case et retourne la reponse JSON. Ajouter les annotations OpenAPI pour Swagger.

6

Tests

Ecrire les tests unitaires du use case (mock des ports), les tests d'integration du repository (base PostgreSQL de test) et les tests fonctionnels du controller (WebTestCase). Verifier la couverture avec PHPUnit.