Appearance
Plan backend collection, pricing et alertes
Date : 2026-05-18. Statut : plan d'architecture produit/backend avant implémentation des providers pricing.
SKN doit devenir plus qu'un scanner. Le scanner identifie l'objet ; les moteurs collection, pricing et alertes transforment cet objet en portfolio privé qui aide l'utilisateur à décider quand acheter, conserver, vendre ou mettre en vente.
Ce plan sépare volontairement la fondation backend de la formule pricing provider-specific. Le modèle exact de pondération doit attendre la documentation pricing dédiée. L'architecture ci-dessous définit où ce modèle tournera et quelles données il devra consommer.
Thèse produit
La boucle utilisateur coeur :
text
scanner ou rechercher un item
-> confirmer identité, condition, langue, grade, quantité
-> ajouter à la collection privée
-> attacher prix d'acquisition et preuves/photos
-> calculer valorisation et confiance
-> suivre les mouvements dans le temps
-> notifier l'utilisateur sur un pump/drop/opportunité significatifL'app doit se rapprocher d'un terminal portfolio plutôt que d'un classeur statique. Mais elle ne doit pas prétendre être un marché financier si la donnée sous-jacente est faible. Chaque valorisation et alerte doit afficher fraîcheur, confiance et explication des sources.
Limite de scope
Dans le scope
- collections utilisateur privées ;
- cartes raw, slabs et produits scellés comme classes d'objets séparées ;
- prix d'acquisition et gain/perte portfolio ;
- observations marché depuis des providers normalisés ;
- estimations pondérées avec confiance ;
- snapshots de valorisation par holding et portfolio ;
- règles d'alertes et événements d'alerte pour drops, pumps et items surveillés ;
- adapters provider derrière des contrats normalisés ;
- audit trail pour les changements sensibles de collection.
Hors scope pour maintenant
- checkout marketplace, paiement, payout, escrow, litiges ;
- partage public de collection par défaut ;
- exécution automatique achat/vente ;
- scraping non revu comme dépendance production ;
- redistribution publique d'images ou données marché que les providers n'autorisent pas ;
- promesse d'alertes temps réel si les quotas provider imposent du polling.
Domaines backend
1. Domaine utilisateur et collection
Le Collection Engine répond : "Que possède cet utilisateur ?"
Tables coeur :
| Table | Rôle |
|---|---|
users | Identité compte. Peut démarrer simple, mais les holdings doivent être user-scoped dès le premier jour. |
collection_items | Une ligne de possession : référence item, type, quantité, condition, langue, grade, état scellé. |
collection_item_photos | Photos preuve utilisateur, photos affichables, captures scan. |
collection_item_acquisitions | Prix d'achat, date d'achat, vendeur/source, shipping, frais, notes. |
collection_item_events | Audit trail : création, édition, quantité changée, vendu, supprimé, restauré. |
collection_item_valuation_snapshots | Valeur courante et historique d'un holding. |
collection_exports | Jobs d'export explicites pour les données utilisateur. |
Exigences privacy :
- collection privée par défaut ;
- chaque requête est scopée par user ID ;
- les labels de localisation/stockage ne sont jamais publics par défaut ;
- le prix d'acquisition est privé et ne doit pas fuiter dans une future annonce marketplace ;
- l'EXIF doit être supprimé des photos uploadées ;
- les suppressions doivent d'abord être soft-delete, avec restore/audit.
Pour une app professionnelle, le backend distant doit être canonique. Le mobile pourra cacher localement plus tard, mais le backend doit posséder identité, audit, valorisation, alertes et état des refresh providers.
2. Domaine identité item
Les moteurs Pricing et Collection ne doivent pas stocker directement les formes provider brutes. Ils référencent des identités SKN normalisées.
Identités coeur :
| Classe item | Champs d'identité |
|---|---|
raw_card | TCG, set, numéro collector, langue, variante, finish, condition. |
graded_card / slab | identité carte intérieure, grader, grade, numéro certificat, metadata label. |
sealed_product | type produit, set/série, langue, région, SKU/code barre quand disponible. |
L'Identity Engine reste responsable de résoudre un scan vers une de ces classes. Le Collection Engine stocke l'instance possédée par l'utilisateur.
3. Domaine observations pricing
Le Pricing Engine répond : "Quelles preuves marché existent ?"
Tables coeur :
| Table | Rôle |
|---|---|
price_sources | Registre provider : Cardmarket, eBay, TCGPlayer, PriceCharting, APIs provider, imports manuels. |
price_provider_items | Mapping entre identité SKN et SKU/listing/category provider. |
price_observations | Observation marché normalisée : sold comp, listing actif, ask, comp graded, comp scellé. |
price_observation_fees | Shipping, frais acheteur, frais vendeur, taxes quand connus. |
price_observation_flags | Outlier, low-confidence, mauvaise langue, damaged, incomplet, suspect. |
price_import_runs | Suivi des jobs d'ingest, fraîcheur provider, erreurs, quotas. |
Chaque observation doit garder :
- source et URL source ou ID externe ;
- vendu vs listing actif ;
- prix et devise ;
- traitement shipping/frais ;
- date vente/listing ;
- région ;
- langue ;
- condition ;
- classe item ;
- grader et grade si pertinent ;
- pointeur payload brut ou checksum pour audit ;
- statut terms/licensing pour cache/affichage.
Les adapters provider spécifiques peuvent arriver plus tard, mais le schéma d'observation doit exister avant d'ajouter beaucoup de providers.
4. Domaine estimation pricing
Le Pricing Engine répond : "Quelle est la valeur estimée actuelle ?"
Tables coeur :
| Table | Rôle |
|---|---|
price_estimates | Estimation courante pour une identité SKN et un contexte marché. |
price_estimate_components | Breakdown par source/signal : Cardmarket, eBay sold, listings actifs, source graded, override manuel. |
price_estimate_history | Série temporelle des estimations pour charts et alertes. |
price_estimate_explanations | Payload explicatif lisible par l'app. |
La sortie d'estimation doit inclure :
- estimation low / mid / high ;
- devise ;
- score de confiance ;
- score de liquidité ;
- taille d'échantillon ;
- fraîcheur ;
- sources les plus fortes ;
- warnings : donnée stale, peu de comps, mismatch langue, mismatch grade, beaucoup d'outliers.
Ne pas démarrer par une "moyenne magique". La première version peut être simple, mais elle doit être explicable :
- les comps vendus pèsent plus que les listings actifs ;
- les observations récentes pèsent plus que les anciennes ;
- les signaux EU/FR pèsent plus pour les utilisateurs FR/EU que les données US-only ;
- les matchs exacts langue/condition/grade pèsent plus que les mismatches ;
- les listings actifs peuvent informer la pression ask mais ne doivent pas dominer la valeur vendue ;
- un faible échantillon réduit la confiance même si les prix semblent hauts.
5. Domaine valorisation
Le Valuation Engine applique les estimations de prix aux vrais holdings utilisateur.
Il répond :
- valeur totale portfolio ;
- valeur par classe item : raw, slab, scellé ;
- gain/perte non réalisé vs prix d'acquisition ;
- top movers ;
- nombre de valorisations stale ;
- nombre de valorisations low-confidence.
Tables coeur :
| Table | Rôle |
|---|---|
portfolio_snapshots | Valeur portfolio agrégée par utilisateur dans le temps. |
collection_item_valuation_snapshots | Valeur estimée par holding dans le temps. |
portfolio_breakdowns | Split par classe item, TCG, set, condition, grade, langue. |
Cette couche ne doit pas recalculer les providers en live à chaque écran mobile. Elle doit lire des estimations et snapshots pré-calculés, puis afficher la fraîcheur.
6. Domaine alertes
L'Alert Engine répond : "Qu'est-ce qui a assez changé pour compter ?"
Types d'alertes :
| Alerte | Déclencheur |
|---|---|
| Price drop | Item surveillé ou wishlist passe sous un seuil. |
| Price pump | Item possédé dépasse un seuil de mouvement. |
| Portfolio mover | Un holding devient top mover jour/semaine. |
| Spread opportunity | Les comps vendus et listings actifs créent une possible opportunité achat/vente. |
| Donnée stale | Holding important sans donnée marché fraîche. |
| Pricing manquant | Item possédé sans estimation. |
Tables coeur :
| Table | Rôle |
|---|---|
alert_rules | Règles définies par l'utilisateur ou suggérées par le système. |
alert_rule_targets | Item, holding, recherche, set, classe produit ou segment portfolio. |
alert_events | Alertes déclenchées avec payload et IDs d'estimation source. |
alert_deliveries | État de delivery push/email/in-app et retry. |
alert_deduplication_keys | Cooldowns pour éviter le spam sur feeds bruités. |
Quasi temps réel doit vouloir dire "assez frais pour un comportement marketplace", pas faux tick-by-tick trading. Les permissions et quotas provider décident si on fait du polling, webhook, batch refresh ou hybride. Le backend doit afficher la fraîcheur honnêtement.
Cadence de rafraîchissement des prix
La fraîcheur pricing doit être organisée par tiers. Pokémon contient trop de cartes, variantes, slabs et produits scellés pour tout rafraîchir en continu.
Contraintes source actuelles :
- Cardmarket documente son price guide public comme mis à jour une fois par jour, tandis que le catalogue produit est mis à jour quand de nouvelles releases sont ajoutées. Cardmarket est donc une baseline quotidienne solide, pas une source tick-by-tick. Source : annonce Cardmarket du 2024-06-05.
- Les APIs eBay Browse permettent de rechercher des listings actifs et des cas de découverte/refinement, mais l'accès à l'historique des ventes est contraint. eBay documente beaucoup de Buy APIs comme limited release et précise que Marketplace Insights est restreint et pas ouvert aux nouveaux utilisateurs pour le moment. La Feed API eBay peut fournir des snapshots horaires d'items modifiés, mais elle est aussi limited release. Sources : overview eBay Buy APIs, support Buy API par marketplace, ressources Feed API.
Tiers de refresh recommandés :
| Tier | Cadence | Scope | Objectif |
|---|---|---|---|
| Baseline marché complète | Quotidien | Price guide Cardmarket, feeds provider quotidiens, catalogue large | Recalculer les estimations stables et le long tail. |
| Portfolio utilisateur | Toutes les 1 à 6 h | Items possédés, holdings chers, items récemment scannés | Donner une sensation portfolio vivant sans rafraîchir tout le marché. |
| Watchlist / alertes actives | Toutes les 30 à 60 min si le budget provider le permet | Items surveillés, wishlist, items avec règles d'alerte actives | Capturer les mouvements utiles à l'utilisateur plus vite que la baseline quotidienne. |
| Hot queue | Toutes les 5 à 30 min quand c'est légalement et techniquement autorisé | Items qui viennent de bouger fort ou très surveillés | Permettre un comportement quasi temps réel pump/drop sans prétendre que tout le marché est live. |
| Long tail | Quotidien ou plus lent | Items sans propriétaire, sans watch, faible liquidité | Éviter de brûler les quotas provider. |
L'app doit afficher la fraîcheur à côté des estimations et alertes. Une alerte utile avec "mis à jour il y a 22 min" est crédible ; une alerte stale sans fraîcheur crée une fausse confiance.
Modèle d'évaluation des alertes
Les alertes doivent partir depuis les changements d'estimation, pas depuis une observation provider brute isolée.
Flow d'évaluation :
text
nouvelles observations ingérées
-> estimation recalculée
-> historique estimation mis à jour
-> alert evaluator compare aux baselines
-> gates confiance et liquidité appliqués
-> déduplication/cooldown vérifiés
-> alerte in-app/push crééeBaselines de comparaison :
- dernière estimation fiable ;
- moyenne 7 jours ;
- moyenne 30 jours ;
- prix d'acquisition utilisateur quand l'item est possédé ;
- seuil utilisateur pour watchlist ou wishlist.
Seuils d'alerte par défaut :
| Alerte | Défaut recommandé |
|---|---|
| Price drop | -10% à -15% vs moyenne 7 jours, ou sous un seuil utilisateur. |
| Price pump | +15% à +25% vs moyenne 7 jours, avec assez de liquidité. |
| Portfolio mover | Le holding entre dans les top movers jour/semaine de l'utilisateur. |
| Spread opportunity | Listing actif nettement sous la valeur vendue/estimée récente. |
Gates qualité avant notification :
- nombre minimum d'observations ou source fiable forte ;
- fraîcheur source dans le SLA du tier d'alerte ;
- confiance estimation au-dessus du minimum de la règle ;
- mouvement au-dessus du bruit en pourcentage et en valeur absolue ;
- condition/langue/grade suffisamment cohérents ;
- les outliers suspects ne dominent pas l'estimation ;
- pas d'alerte dupliquée dans la fenêtre de cooldown.
Exemple de payload alerte :
json
{
"type": "price_pump",
"title": "Dracaufeu 4/102 FR monte de 18%",
"itemId": "raw_card:pokemon:base1:4:fr",
"estimate": { "mid": 182.0, "currency": "EUR", "confidence": 0.76 },
"movement": { "window": "7d", "percent": 18.0, "absolute": 27.8 },
"primarySignals": ["ebay_sold", "cardmarket_daily"],
"freshness": "2h",
"action": "review_sell_or_list"
}Le wording produit doit aider la décision sans faire du conseil financier. Préférer "revoir opportunité de vente/listing" à "vends maintenant".
Stratégie providers et MCP
Les intégrations provider doivent être des adapters runtime backend, pas des dépendances mobile.
Familles de providers candidates :
- Cardmarket pour le contexte marché EU ;
- eBay sold comps et listings actifs ;
- TCGPlayer ou APIs provider pour le contexte US ;
- PriceCharting ou providers graded pour slabs ;
- plus tard Vinted / Leboncoin comme signaux peer-to-peer français si les terms l'autorisent.
Les connecteurs MCP sont utiles pour l'outillage interne, les opérations, les backfills et l'exploration provider. Pour le runtime produit, SKN doit quand même stocker des observations normalisées dans sa base backend et servir l'app via les APIs SKN. L'app mobile ne doit jamais dépendre directement des formes provider ou MCP.
Avant usage production, chaque provider demande une revue des terms :
- permission de cache ;
- permission d'affichage ;
- quota de refresh ;
- usage commercial ;
- attribution ;
- rate limits ;
- contraintes de redistribution ;
- possibilité de stocker et afficher des estimations dérivées.
Surface API
Endpoints backend initiaux :
| Endpoint | Rôle |
|---|---|
POST /collection/items | Ajouter un holding depuis scan ou recherche manuelle. |
GET /collection/items | Lister les holdings utilisateur avec filtres. |
PATCH /collection/items/:id | Modifier condition, quantité, notes, stockage, acquisition. |
DELETE /collection/items/:id | Soft-delete d'un holding. |
GET /collection/portfolio | Totaux portfolio et breakdowns. |
GET /pricing/items/:identityId/estimate | Estimation normalisée courante et explication. |
GET /pricing/items/:identityId/observations | Preuves debug/provider quand l'utilisateur a le droit de les voir. |
POST /alerts/rules | Créer une règle d'alerte. |
GET /alerts/events | Feed d'alertes in-app. |
PATCH /alerts/events/:id | Marquer lu, ignoré, actionné. |
Intégration scan :
text
résultat scan
-> écran de confirmation
-> ajout à collection
-> collection item créé
-> snapshot de valorisation attaché si estimation disponible
-> règles d'alerte optionnelles suggéréesRoadmap d'implémentation
Phase 0 — verrouillage architecture
Livrables :
- ADR schéma pour holdings utilisateur, observations pricing, estimations et alertes ;
- checklist terms providers ;
- contrat identité item partagé par collection/pricing/scan ;
- décision sur le modèle auth pour holdings privés.
Phase 1 — fondation collection privée
Livrables :
- tables collection scopées utilisateur ;
- add/edit/delete/list holdings ;
- prix et date d'acquisition ;
- support classes
raw_card,sealed_productet futurgraded_card; - audit trail ;
- tests locaux sur isolation utilisateur et soft-delete.
Phase 2 — ajout depuis scan
Livrables :
- endpoint backend pour créer un holding depuis résultat de scan ;
- flow mobile de confirmation ;
- fallback manuel quand le résultat scan est faux ou ambigu ;
- capture condition, langue, quantité, prix d'acquisition, notes.
Phase 3 — schéma pricing avec provider mock
Livrables :
price_sources,price_observations,price_estimates;- ingestion provider synthétique/mock pour les tests ;
- premier estimateur explicable avec règles déterministes ;
- pas encore de dépendance provider live.
Cette phase prouve le modèle de données avant la complexité Cardmarket/eBay.
Phase 4 — valorisation portfolio
Livrables :
- snapshots de valorisation par holding ;
- totaux portfolio ;
- gain/perte vs acquisition ;
- breakdown raw/slab/scellé ;
- labels stale/low-confidence.
Phase 5 — alertes avec événements marché simulés
Livrables :
- règles d'alerte ;
- détection pump/drop sur historique d'estimations ;
- déduplication et cooldown ;
- feed d'alertes in-app ;
- tests avec mouvements de prix synthétiques.
Cela valide la boucle produit avant les quotas provider réels.
Phase 6 — premier adapter provider réel
Livrables :
- un adapter provider derrière
PriceObservationSource; - observabilité import-run ;
- comportement cache/display compatible terms ;
- table mapping provider ;
- flags de confiance source-specific.
Choisir le premier provider seulement après la documentation pricing dédiée et la revue des terms. Cardmarket/eBay sont de bons candidats, mais le premier adapter production doit être la source avec le meilleur fit légal, quota et qualité data.
Phase 7 — alertes quasi temps réel
Livrables :
- tiers de refresh planifiés selon importance item ;
- refresh prioritaire watchlist ;
- génération événements après update d'estimation ;
- intégration push notification ;
- explications d'alertes montrant source, mouvement, confiance et fraîcheur.
Première slice de dev recommandée
Commencer par la Phase 1 : fondation collection privée.
Pourquoi :
- elle est requise pour chaque feature future de valorisation et d'alerte ;
- elle est testable sans risque provider ;
- elle crée une surface produit immédiate après le scan ;
- elle garde les actifs utilisateur privés dès le début ;
- elle évite de construire le pricing avant d'avoir un endroit où attacher les valorisations.
La première PR backend ne doit pas encore intégrer Cardmarket ou eBay. Elle doit créer les holdings user-scoped et assez de forme API pour "scan -> confirmation -> ajout à collection".
Décisions ouvertes
- Quel modèle auth est acceptable pour le premier backend de collection privée ?
- Les photos collection doivent-elles être stockées d'abord sur le filesystem VPS, en object storage plus tard, ou directement en object storage ?
- Le premier provider pricing doit-il cibler la pertinence EU/Cardmarket ou des sold comps eBay plus larges ?
- Quelle latence minimum rend une alerte utile : minutes, heure, jour ?
- Quel wording évite de promettre du conseil financier tout en aidant fortement les décisions achat/vente ?
- Quelle classe item doit recevoir le pricing en premier : cartes raw, scellés ou slabs ?