Migros possĂšde plusieurs sites web et une application mobile, qui nĂ©cessitent tous dâafficher des donnĂ©es produits. Nous avons dĂ©veloppĂ© M-API, une application Symfony qui rĂ©cupĂšre les donnĂ©es des diffĂ©rents systĂšmes, les nettoie et les indexe dans Elasticsearch. Cet index peut ĂȘtre consultĂ© en temps rĂ©el via une API REST, avec des rĂ©ponses en JSON et XML. La M-API gĂšre plusieurs millions de requĂȘtes par jour, et fonctionne mĂȘme lors des pics de trafic.
Architecture de l'API

Les documents Elasticsearch sont volumineux et contiennent de nombreuses donnĂ©es imbriquĂ©es. Nous avons dĂ©cidĂ© de dĂ©normaliser toutes les donnĂ©es produits directement dans chaque document produit pour accĂ©lĂ©rer les recherches. Lorsque nâimporte quelle partie des informations dâun produit change, nous reconstruisons les donnĂ©es et rĂ©-indexons le produit. LâAPI offre des paramĂštres de requĂȘte permettant aux clients de contrĂŽler le niveau de dĂ©tail souhaitĂ©.
Lâapplication Symfony est conçue autour de modĂšles de domaine reprĂ©sentant les donnĂ©es. Lors de lâindexation, les modĂšles sont remplis avec les donnĂ©es des systĂšmes externes, puis sĂ©rialisĂ©s dans Elasticsearch. Lors des requĂȘtes, les modĂšles sont restaurĂ©s Ă partir des rĂ©ponses dâElasticsearch, puis re-sĂ©rialisĂ©s dans le format demandĂ© avec le niveau de dĂ©tail souhaitĂ©.
Nous utilisons JMS Serializer, qui permet de grouper les champs pour ajuster le niveau de dĂ©tail et gĂ©rer plusieurs versions dâAPI Ă partir dâune seule source de donnĂ©es. Pour amĂ©liorer les performances, nous avons développé notre propre sérialiseur pour JMS.
Il y a bien sûr un coût en performance dû à la désérialisation et re-sérialisation des données Elasticsearch, mais la flexibilité obtenue compense largement ce coût.
Au niveau frontend, nous utilisons des serveurs Varnish pour mettre en cache les rĂ©ponses. Nous nous appuyons sur FOSHttpCacheBundle pour gĂ©rer les en-tĂȘtes de cache et invalider les donnĂ©es lorsque câest nĂ©cessaire.
Une API indépendante
La M-API est une application Symfony autonome, qui ne gĂ©nĂšre pas de rendu HTML. Son seul objectif est de collecter, normaliser et indexer les donnĂ©es, puis de les fournir via une API REST. Cela permet de dĂ©velopper diffĂ©rentes plateformes qui exploitent les mĂȘmes donnĂ©es.
Les systĂšmes qui utilisent la M-API incluent un site de catalogue produit, une application mobile, une plateforme communautaire pour les clients et divers sites marketing dĂ©diĂ©s Ă des gammes de produits spĂ©cifiques. Chaque plateforme peut ĂȘtre dĂ©veloppĂ©e indĂ©pendamment, avec des technologies diffĂ©rentes de PHP. Alors que le catalogue de produits a Ă©tĂ© mis en Ćuvre comme une application Symfony, l'app mobile est une application native IOS et Android, et de nombreux sites de marketing sont rĂ©alisĂ©s avec le CMS Magnolia basĂ© sur Java.

GrĂące Ă la M-API, ces applications web peuvent se concentrer sur leur utilisation principale, sans avoir Ă rĂ©implĂ©menter des systĂšmes dâimportation de donnĂ©es depuis diffĂ©rentes sources. Cela rĂ©duit lâeffort et le temps de dĂ©veloppement, tout en amĂ©liorant la qualitĂ© des donnĂ©es, car les corrections et amĂ©liorations sont centralisĂ©es dans la M-API.
La gestion des données dans est plutÎt complexe. La M-API collecte:
- les données de base des produits provenant du catalogue central des produits et des systÚmes de prix
- les images préparées pour le web via le CDN rokka.io
- Métadonnées telles que les avertissements chimiques ou les recommandations de produits provenant d'analyses de data warehouse
- les informations sur les stocks provenant du systĂšme de gestion des stocks ainsi qu'une recherche de magasin
- les données de popularité basées sur les commentaires des clients et Google Analytics
Processus de développement
Avec le client, nous avons choisi un processus agile. Le minimum viable product (MVP) Ă©tait une API fournissant des donnĂ©es produits en JSON et XML. AprĂšs quelques mois, lâAPI a atteint un niveau de fonctionnalitĂ© suffisant et a Ă©tĂ© mise en ligne. Au cours des 10 derniĂšres annĂ©es, nous avons ajoutĂ© de nouvelles sources de donnĂ©es et de nouveaux endpoints API, avec des releases toutes les deux semaines.
Pour garantir une API stable, nous avons mis en place une versioning dâAPI. Chaque requĂȘte doit inclure un en-tĂȘte indiquant la version. Cela permet aux client·e·s de mettre Ă jour Ă leur propre rythme. GrĂące au JMS Serializer, nous avons pu adapter les formats de donnĂ©es sans dupliquer les donnĂ©es. Nous avons Ă©galement tenu un changelog pour aider les client·e·s Ă suivre les Ă©volutions
Pour déployer de nouvelles fonctionnalités sans les exposer prématurément, nous avons utilisé des feature flags.
Architecture dâindexation
Lors de lâindexation, nous dĂ©normalisons une grande quantitĂ© de donnĂ©es pour optimiser la vitesse des requĂȘtes. Par exemple, nous stockons lâintĂ©gralitĂ© du breadcrumb des catĂ©gories produit directement dans chaque document produit, pour Ă©viter dâavoir Ă exĂ©cuter plusieurs requĂȘtes Elasticsearch.
Pour gĂ©rer ce volume de donnĂ©es, nous avons copiĂ© les donnĂ©es lentes Ă interroger dans une base MySQL locale, servant essentiellement de cache. Nous avons aussi utilisĂ© Redis pour stocker les donnĂ©es intermĂ©diaires, accĂ©lĂ©rant encore le processus. Enfin, nous avons sĂ©parĂ© le processus en deux Ă©tapes : collecte des donnĂ©es puis indexation. Elasticsearch fonctionne en cluster avec plusieurs nĆuds, car lâindexation impose une forte charge sur le systĂšme.
Le processus dâimportation des donnĂ©es exĂ©cute des commandes Symfony qui mettent Ă jour la base MySQL. Ces opĂ©rations sont parallĂ©lisĂ©es Ă lâaide de RabbitMQ. Une fois la file de messages vide, lâindexation Elasticsearch commence Ă©galement en mode parallĂšle.
Un dĂ©fi majeur Ă©tait la gestion des changements de schĂ©ma Elasticsearch. Lâindex contient des centaines de milliers de documents, reconstruire un index complet prend donc du temps.
Pour optimiser cela:
- le nouveau code est dĂ©ployĂ© sans ĂȘtre mis en ligne
- un nouvel index est créé en copiant les documents de l'ancien index (plus rapide qu'une réimportation complÚte)
- une fois terminé, le nouvel index est activé.
Cette stratégie permet d'économiser temps et ressources systÚme.