Concevoir une base de données Firestore à partir de zéro est difficile. Vous devez penser aux requêtes, à la dénormalisation, aux sous-collections et à une douzaine d'autres préoccupations spécifiques à NoSQL avant d'écrire votre première ligne de code. Au lieu de partir de zéro, utilisez ces 10 modèles éprouvés comme modèles. Chacun représente une architecture d'application courante — e-commerce, chat, SaaS multi-tenant, services basés sur la localisation — avec un JSON Schema complet montrant exactement comment modéliser les collections. Copiez le modèle qui correspond à votre cas d'utilisation, adaptez-le à vos besoins, et vous aurez une base solide en minutes plutôt qu'en semaines.
1. Profils utilisateur
Applications sociales, plateformes SaaS, sites d'adhésion
Collection Structure
firestore_structures.pattern1_structure
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"collection": "users",
"description": "User profiles with authentication info",
"schema": {
"type": "object",
"properties": {
"displayName": {
"type": "string",
"description": "User's full name"
},
"email": {
"type": "string",
"format": "email",
"description": "Login email address"
},
"photoURL": {
"type": "string",
"format": "uri",
"description": "Profile picture URL"
},
"role": {
"type": "string",
"enum": ["admin", "editor", "viewer"],
"description": "Access control role"
},
"createdAt": {
"type": "string",
"format": "date-time",
"description": "Account creation timestamp"
}
},
"required": ["displayName", "email", "role", "createdAt"]
}
}Why this design: Stockez les profils utilisateur comme documents au niveau racine avec l'UID Firebase Auth comme ID de document. Dénormalisez les champs fréquemment consultés comme displayName et photoURL pour qu'ils puissent être intégrés dans d'autres collections sans lectures supplémentaires.
Common mistake: Ne mettez pas les paramètres ou préférences utilisateur en sous-collections sauf s'ils ont des centaines d'éléments. Gardez le document utilisateur léger et rapide à lire.
2. Produits et commandes e-commerce
Boutiques en ligne, marketplaces, systèmes d'inventaire
Collection Structure
firestore_structures.pattern2_structure
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"collection": "orders",
"description": "Customer purchase orders",
"schema": {
"type": "object",
"properties": {
"userId": {
"type": "string",
"description": "Reference to users collection"
},
"items": {
"type": "array",
"description": "Ordered items with denormalized product data",
"items": {
"type": "object",
"properties": {
"productId": { "type": "string" },
"name": { "type": "string" },
"price": { "type": "number", "minimum": 0 },
"quantity": { "type": "integer", "minimum": 1 }
},
"required": ["productId", "name", "price", "quantity"]
}
},
"total": {
"type": "number",
"minimum": 0,
"description": "Order total in USD"
},
"status": {
"type": "string",
"enum": ["pending", "confirmed", "shipped", "delivered"],
"description": "Current order status"
},
"createdAt": {
"type": "string",
"format": "date-time",
"description": "Order timestamp"
}
},
"required": ["userId", "items", "total", "status", "createdAt"]
}
}Why this design: Stockez les commandes comme documents au niveau racine avec userId pour le filtrage. Dénormalisez le nom et le prix du produit dans les articles de commande pour que les commandes historiques restent exactes même si les données produit changent plus tard.
Common mistake: Ne stockez pas les compteurs d'inventaire dans les documents produit — utilisez une collection d'inventaire séparée ou Cloud Functions pour éviter les conditions de concurrence sur les produits populaires.
3. Chat et messagerie
Applications de chat en temps réel, support client, outils de collaboration
Collection Structure
firestore_structures.pattern3_structure
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"collection": "messages",
"description": "Chat messages within a room",
"schema": {
"type": "object",
"properties": {
"authorId": {
"type": "string",
"description": "User ID of message author"
},
"authorName": {
"type": "string",
"description": "Denormalized author display name"
},
"text": {
"type": "string",
"description": "Message content",
"maxLength": 5000
},
"createdAt": {
"type": "string",
"format": "date-time",
"description": "Message timestamp"
},
"edited": {
"type": "boolean",
"description": "Whether message was edited"
}
},
"required": ["authorId", "authorName", "text", "createdAt"]
}
}Why this design: Utilisez des sous-collections pour les messages afin que chaque salon de chat puisse contenir un nombre illimité de messages. Dénormalisez authorName pour éviter de récupérer les profils utilisateur pour chaque affichage de message. Stockez lastMessage dans le chatRoom parent pour un rendu rapide de l'aperçu.
Common mistake: N'essayez pas d'implémenter les accusés de lecture ou les indicateurs de saisie dans les documents Firestore — utilisez Realtime Database ou Firebase Cloud Messaging pour les données de présence éphémères.
4. Blog avec commentaires
Plateformes de contenu, CMS, forums communautaires
Collection Structure
firestore_structures.pattern4_structure
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"collection": "posts",
"description": "Blog posts with metadata",
"schema": {
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Post title",
"maxLength": 200
},
"content": {
"type": "string",
"description": "Post body in markdown"
},
"authorId": {
"type": "string",
"description": "Reference to users collection"
},
"authorName": {
"type": "string",
"description": "Denormalized author name"
},
"publishedAt": {
"type": "string",
"format": "date-time",
"description": "Publication timestamp"
},
"commentCount": {
"type": "integer",
"minimum": 0,
"description": "Cached comment count"
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Post tags for filtering"
}
},
"required": ["title", "content", "authorId", "authorName", "publishedAt"]
}
}Why this design: Stockez les articles comme documents racine avec des informations d'auteur dénormalisées pour un rendu de liste efficace. Gardez un commentCount mis en cache via Cloud Functions pour éviter de lire toute la sous-collection de commentaires juste pour afficher un compte.
Common mistake: Ne stockez pas la liste complète des commentaires dans le document de l'article sous forme de tableau — cela casse une fois que vous avez plus de quelques dizaines de commentaires et rend la pagination impossible.
5. SaaS multi-tenant
Applications B2B, outils basés sur l'espace de travail, gestion d'équipe
Collection Structure
firestore_structures.pattern5_structure
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"collection": "organizations",
"description": "Tenant organizations for multi-tenant SaaS",
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Organization name"
},
"plan": {
"type": "string",
"enum": ["free", "pro", "enterprise"],
"description": "Subscription plan"
},
"ownerId": {
"type": "string",
"description": "User ID of organization owner"
},
"createdAt": {
"type": "string",
"format": "date-time",
"description": "Organization creation timestamp"
},
"memberCount": {
"type": "integer",
"minimum": 1,
"description": "Cached member count"
}
},
"required": ["name", "plan", "ownerId", "createdAt"]
}
}Why this design: Utilisez les organisations comme collection de niveau supérieur avec toutes les données tenant imbriquées en dessous. Cela simplifie les règles de sécurité : les utilisateurs ne peuvent accéder qu'aux données au sein de leur organisation. Stockez les rôles de membre dans une sous-collection pour une énumération facile.
Common mistake: Ne dupliquez pas les données d'organisation dans les documents utilisateur — cela crée des problèmes de synchronisation. Au lieu de cela, interrogez la collection organizations filtrée par appartenance utilisateur.
6. Journal d'activité et d'audit
Suivi de conformité, flux d'activité utilisateur, tableaux de bord admin
Collection Structure
firestore_structures.pattern6_structure
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"collection": "activityLog",
"description": "Audit trail of user actions",
"schema": {
"type": "object",
"properties": {
"userId": {
"type": "string",
"description": "User who performed the action"
},
"userName": {
"type": "string",
"description": "Denormalized user name"
},
"action": {
"type": "string",
"enum": ["created", "updated", "deleted", "viewed"],
"description": "Action type"
},
"resourceType": {
"type": "string",
"description": "Type of resource affected (e.g., 'post', 'user')"
},
"resourceId": {
"type": "string",
"description": "ID of affected resource"
},
"metadata": {
"type": "object",
"description": "Additional context data"
},
"timestamp": {
"type": "string",
"format": "date-time",
"description": "When the action occurred"
}
},
"required": ["userId", "action", "resourceType", "resourceId", "timestamp"]
}
}Why this design: Stockez les journaux d'activité comme collection racine avec des champs indexés pour userId, action et timestamp. Générez des entrées de journal via Cloud Functions sur les écritures de documents pour garantir des pistes d'audit complètes même si le code client échoue.
Common mistake: N'essayez pas de stocker les journaux comme sous-collections sous les utilisateurs ou les ressources — cela rend les requêtes inter-utilisateurs ou inter-ressources impossibles. Utilisez une collection plate avec des champs de référence.
7. Géolocalisation et cartes
Services basés sur la localisation, applications de livraison, localisateurs de magasins
Collection Structure
firestore_structures.pattern7_structure
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"collection": "locations",
"description": "Locations with geospatial data",
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Location name"
},
"address": {
"type": "string",
"description": "Street address"
},
"geopoint": {
"type": "object",
"description": "Firestore GeoPoint",
"properties": {
"latitude": { "type": "number", "minimum": -90, "maximum": 90 },
"longitude": { "type": "number", "minimum": -180, "maximum": 180 }
},
"required": ["latitude", "longitude"]
},
"geohash": {
"type": "string",
"description": "Geohash for proximity queries"
},
"category": {
"type": "string",
"enum": ["restaurant", "store", "office", "other"],
"description": "Location type"
}
},
"required": ["name", "geopoint", "geohash"]
}
}Why this design: Stockez des chaînes geohash à côté des champs GeoPoint pour permettre des requêtes de proximité efficaces. Firestore ne peut pas faire de recherches de rayon natives, donc geohash vous permet de filtrer par préfixe pour trouver des emplacements à proximité. Utilisez des bibliothèques comme geofire-common pour générer des hashes.
Common mistake: N'essayez pas d'interroger par plages latitude/longitude directement — Firestore ne peut pas faire de requêtes d'inégalité composées. Utilisez toujours des préfixes geohash pour le filtrage basé sur la localisation.
8. Gestion des stocks
Systèmes d'entrepôt, suivi des stocks, applications de chaîne d'approvisionnement
Collection Structure
firestore_structures.pattern8_structure
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"collection": "inventory",
"description": "Inventory items with stock levels",
"schema": {
"type": "object",
"properties": {
"sku": {
"type": "string",
"description": "Stock keeping unit code"
},
"name": {
"type": "string",
"description": "Item name"
},
"quantity": {
"type": "integer",
"minimum": 0,
"description": "Current stock quantity"
},
"warehouseId": {
"type": "string",
"description": "Warehouse location reference"
},
"reorderLevel": {
"type": "integer",
"minimum": 0,
"description": "Quantity threshold for reorder alerts"
},
"lastRestocked": {
"type": "string",
"format": "date-time",
"description": "Last restock timestamp"
}
},
"required": ["sku", "name", "quantity", "warehouseId"]
}
}Why this design: Utilisez des transactions lors de la mise à jour de la quantité pour éviter les conditions de concurrence. Pour un inventaire à volume élevé avec des mises à jour fréquentes, envisagez d'utiliser des compteurs distribués ou une collection de journal de transactions séparée pour éviter la contention d'écriture.
Common mistake: N'incrémentez/décrémentez pas les champs de quantité depuis le code client sans transactions — vous obtiendrez des comptages incorrects sous accès concurrent. Utilisez Cloud Functions ou une logique côté serveur.
9. Fil social et abonnés
Réseaux sociaux, fils d'actualités, flux d'activité
Collection Structure
firestore_structures.pattern9_structure
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"collection": "posts",
"description": "Social feed posts",
"schema": {
"type": "object",
"properties": {
"authorId": {
"type": "string",
"description": "User ID of post author"
},
"authorName": {
"type": "string",
"description": "Denormalized author display name"
},
"content": {
"type": "string",
"description": "Post content",
"maxLength": 5000
},
"likeCount": {
"type": "integer",
"minimum": 0,
"description": "Cached like count"
},
"createdAt": {
"type": "string",
"format": "date-time",
"description": "Post timestamp"
}
},
"required": ["authorId", "authorName", "content", "createdAt"]
}
}Why this design: Stockez les abonnés et les abonnements comme sous-collections séparées sous chaque utilisateur pour permettre des requêtes efficaces dans les deux directions. Gardez un likeCount mis en cache sur les articles mis à jour via Cloud Functions au lieu de lire toute la sous-collection likes.
Common mistake: Ne diffusez pas les articles à tous les fils d'abonnés lors de l'écriture — cela ne passe pas à l'échelle. Au lieu de cela, interrogez les articles des utilisateurs suivis en temps réel en utilisant une requête array-contains sur les ID d'abonnement.
10. IoT et données de capteurs
Tableaux de bord IoT, surveillance environnementale, télémétrie d'appareils
Collection Structure
firestore_structures.pattern10_structure
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"collection": "readings",
"description": "Sensor readings from IoT devices",
"schema": {
"type": "object",
"properties": {
"temperature": {
"type": "number",
"description": "Temperature in Celsius"
},
"humidity": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Relative humidity percentage"
},
"batteryLevel": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Battery percentage"
},
"timestamp": {
"type": "string",
"format": "date-time",
"description": "Reading timestamp"
}
},
"required": ["timestamp"]
}
}Why this design: Stockez les données de séries temporelles comme sous-collections sous chaque appareil. Pour les données haute fréquence, groupez les écritures et implémentez un nettoyage TTL via Cloud Functions pour éviter une croissance illimitée. Envisagez les politiques TTL Cloud Firestore ou les exportations BigQuery pour les analyses à long terme.
Common mistake: Ne stockez pas chaque lecture de capteur si votre appareil rapporte chaque seconde — vous dépasserez les limites d'écriture et les coûts de Firestore. Agrégez en intervalles de minutes ou d'heures, ou utilisez Realtime Database pour les écritures haute fréquence.
Transformez ces modèles en documentation
Ces JSON Schemas ne sont pas que des exemples — ce sont des documentations fonctionnelles que vous pouvez utiliser dans votre projet. Enregistrez-les comme fichiers .schema.json dans votre dépôt, rendez-les avec FireSchema, et donnez à votre équipe une référence navigable qu'elle utilisera réellement. Cela prend 5 minutes à configurer.
Prochaines étapes
Maintenant que vous avez des modèles avec lesquels travailler, apprenez à les utiliser efficacement :