Diseñar una base de datos Firestore desde cero es difícil. Necesitas pensar en consultas, desnormalización, subcolecciones y una docena de otras preocupaciones específicas de NoSQL antes de escribir tu primera línea de código. En lugar de empezar desde cero, usa estos 10 patrones probados como plantillas. Cada uno representa una arquitectura de app común — e-commerce, chat, SaaS multi-tenant, servicios basados en ubicación — con un JSON Schema completo que muestra exactamente cómo modelar las colecciones. Copia el patrón que se ajuste a tu caso de uso, adáptalo a tus necesidades y tendrás una base sólida en minutos en lugar de semanas.
1. Perfiles de Usuario
Apps sociales, plataformas SaaS, sitios de membresía
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: Almacena perfiles de usuario como documentos de nivel raíz con el UID de Firebase Auth como ID del documento. Desnormaliza campos de acceso frecuente como displayName y photoURL para que puedan incrustarse en otras colecciones sin lecturas extra.
Common mistake: No anides configuraciones o preferencias de usuario como subcolecciones a menos que tengan cientos de elementos. Mantén el documento de usuario ligero y rápido de leer.
2. Productos y Pedidos de E-Commerce
Tiendas online, marketplaces, sistemas de inventario
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: Almacena pedidos como documentos de nivel raíz con userId para filtrado. Desnormaliza nombre y precio del producto en los elementos del pedido para que los pedidos históricos se mantengan precisos incluso si los datos del producto cambian después.
Common mistake: No almacenes conteos de inventario en documentos de producto — usa una colección de inventario separada o Cloud Functions para evitar condiciones de carrera en productos populares.
3. Chat y Mensajería
Apps de chat en tiempo real, soporte al cliente, herramientas de colaboración
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: Usa subcolecciones para mensajes para que cada sala de chat pueda contener mensajes ilimitados. Desnormaliza authorName para evitar obtener perfiles de usuario para cada mensaje mostrado. Almacena lastMessage en el chatRoom padre para renderizado rápido de vista previa.
Common mistake: No intentes implementar confirmaciones de lectura o indicadores de escritura en documentos de Firestore — usa Realtime Database o Firebase Cloud Messaging para datos de presencia efímera.
4. Blog con Comentarios
Plataformas de contenido, CMS, foros comunitarios
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: Almacena posts como documentos raíz con información de autor desnormalizada para renderizado eficiente de listas. Mantén un commentCount en caché actualizado mediante Cloud Functions para evitar leer toda la subcolección de comentarios solo para mostrar un conteo.
Common mistake: No almacenes la lista completa de comentarios en el documento del post como un array — se rompe una vez que tienes más de unas pocas docenas de comentarios y hace imposible la paginación.
5. SaaS Multi-Tenant
Aplicaciones B2B, herramientas basadas en espacios de trabajo, gestión de equipos
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: Usa organizations como la colección de nivel superior con todos los datos del tenant anidados debajo. Esto hace que las reglas de seguridad sean simples: los usuarios solo pueden acceder a datos dentro de su organización. Almacena roles de miembros en una subcolección para enumeración fácil.
Common mistake: No dupliques datos de organización en documentos de usuario — crea problemas de sincronización. En su lugar, consulta la colección organizations filtrada por membresía de usuario.
6. Actividad y Log de Auditoría
Seguimiento de cumplimiento, feeds de actividad de usuario, paneles de administración
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: Almacena logs de actividad como una colección raíz con campos indexados para userId, action y timestamp. Genera entradas de log mediante Cloud Functions en escrituras de documentos para asegurar rastros de auditoría completos incluso si el código del cliente falla.
Common mistake: No intentes almacenar logs como subcolecciones bajo usuarios o recursos — hace imposibles consultas entre usuarios o recursos. Usa una colección plana con campos de referencia.
7. Geolocalización y Mapas
Servicios basados en ubicación, apps de entrega, buscadores de tiendas
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: Almacena strings geohash junto a campos GeoPoint para habilitar consultas de proximidad eficientes. Firestore no puede hacer búsquedas nativas de radio, así que geohash te permite filtrar por prefijo para encontrar ubicaciones cercanas. Usa librerías como geofire-common para generar hashes.
Common mistake: No intentes consultar por rangos de latitud/longitud directamente — Firestore no puede hacer consultas de desigualdad compuesta. Siempre usa prefijos geohash para filtrado basado en ubicación.
8. Gestión de Inventario
Sistemas de almacén, seguimiento de stock, apps de cadena de suministro
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: Usa transacciones al actualizar quantity para prevenir condiciones de carrera. Para inventario de alto volumen con actualizaciones frecuentes, considera usar contadores distribuidos o una colección de log de transacciones separada para evitar contención de escritura.
Common mistake: No incrementes/decrementes campos quantity desde código de cliente sin transacciones — obtendrás conteos incorrectos bajo acceso concurrente. Usa Cloud Functions o lógica del lado del servidor.
9. Feed Social y Seguidores
Redes sociales, feeds de noticias, streams de actividad
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: Almacena followers y following como subcolecciones separadas bajo cada usuario para habilitar consultas eficientes en ambas direcciones. Mantén un likeCount en caché en posts actualizado mediante Cloud Functions en lugar de leer toda la subcolección de likes.
Common mistake: No distribuyas posts a los feeds de todos los seguidores en escritura — no escala. En su lugar, consulta posts de usuarios seguidos en tiempo real usando una consulta array-contains en IDs de following.
10. IoT y Datos de Sensores
Paneles IoT, monitoreo ambiental, telemetría de dispositivos
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: Almacena datos de series temporales como subcolecciones bajo cada dispositivo. Para datos de alta frecuencia, agrupa escrituras e implementa limpieza TTL mediante Cloud Functions para evitar crecimiento sin límite. Considera políticas TTL de Cloud Firestore o exportaciones a BigQuery para analíticas de largo plazo.
Common mistake: No almacenes cada lectura de sensor si tu dispositivo reporta cada segundo — excederás los límites de escritura y costos de Firestore. Agrega a intervalos de minutos u horas, o usa Realtime Database para escrituras de alta frecuencia.
Convierte Estos Patrones en Documentación
Estos JSON Schemas no son solo ejemplos — son documentación funcional que puedes usar en tu proyecto. Guárdalos como archivos .schema.json en tu repositorio, renderízalos con FireSchema y dale a tu equipo una referencia navegable que realmente usarán. Toma 5 minutos configurarlo.
Siguientes Pasos
Ahora que tienes patrones con los que trabajar, aprende cómo usarlos efectivamente: