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

L’architecture systùme de la M-API
Le systĂšme fonctionne sur des serveurs redondants. Comme l’API est stateless, il est facile d’ajouter des serveurs supplĂ©mentaires en cas de besoin pour assurer la scalabilitĂ©.

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.

L’ancien site d’infos produits basĂ© sur Symfony n’avait pas de base de donnĂ©es locale et lisait toutes ses donnĂ©es depuis l’API. Il a Ă©tĂ© remplacĂ© par le webshop.

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.