Projetar um banco de dados Firestore do zero é difícil. Você precisa pensar sobre consultas, desnormalização, subcoleções e uma dúzia de outras preocupações específicas do NoSQL antes de escrever sua primeira linha de código. Em vez de começar do zero, use esses 10 padrões comprovados como templates. Cada um representa uma arquitetura comum de app — e-commerce, chat, SaaS multi-tenant, serviços baseados em localização — com um JSON Schema completo mostrando exatamente como modelar as coleções. Copie o padrão que se encaixa no seu caso de uso, adapte às suas necessidades, e você terá uma base sólida em minutos em vez de semanas.
1. Perfis de Usuário
Apps sociais, plataformas SaaS, sites de membros
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: Armazene perfis de usuário como documentos de nível raiz com o UID do Firebase Auth como ID do documento. Desnormalize campos frequentemente acessados como displayName e photoURL para que possam ser incorporados em outras coleções sem leituras extras.
Common mistake: Não aninhe configurações ou preferências do usuário como subcoleções a menos que tenham centenas de itens. Mantenha o documento do usuário enxuto e rápido de ler.
2. E-Commerce: Produtos e Pedidos
Lojas online, marketplaces, sistemas de inventário
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: Armazene pedidos como documentos de nível raiz com userId para filtragem. Desnormalize nome e preço do produto nos itens do pedido para que pedidos históricos permaneçam precisos mesmo se os dados do produto mudarem depois.
Common mistake: Não armazene contagens de inventário em documentos de produto — use uma coleção de inventário separada ou Cloud Functions para prevenir condições de corrida em produtos populares.
3. Chat e Mensagens
Apps de chat em tempo real, suporte ao cliente, ferramentas de colaboração
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: Use subcoleções para mensagens para que cada sala de chat possa conter mensagens ilimitadas. Desnormalize authorName para evitar buscar perfis de usuário para exibir cada mensagem. Armazene lastMessage no chatRoom pai para renderização rápida de preview.
Common mistake: Não tente implementar confirmações de leitura ou indicadores de digitação em documentos Firestore — use Realtime Database ou Firebase Cloud Messaging para dados de presença efêmeros.
4. Blog com Comentários
Plataformas de conteúdo, CMS, fóruns comunitários
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: Armazene posts como documentos raiz com informações desnormalizadas do autor para renderização eficiente de lista. Mantenha um commentCount em cache atualizado via Cloud Functions para evitar ler toda a subcoleção de comentários só para mostrar uma contagem.
Common mistake: Não armazene a lista completa de comentários no documento do post como um array — quebra quando você tem mais de algumas dúzias de comentários e torna paginação impossível.
5. SaaS Multi-Tenant
Aplicações B2B, ferramentas baseadas em workspace, gestão de equipes
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: Use organizations como coleção de nível superior com todos os dados do tenant aninhados abaixo. Isso torna regras de segurança simples: usuários só podem acessar dados dentro de sua organização. Armazene funções de membros em uma subcoleção para enumeração fácil.
Common mistake: Não duplique dados de organização em documentos de usuário — cria problemas de sincronização. Em vez disso, consulte a coleção organizations filtrada por membros do usuário.
6. Log de Atividade e Auditoria
Rastreamento de conformidade, feeds de atividade de usuário, dashboards de 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: Armazene logs de atividade como uma coleção raiz com campos indexados para userId, action e timestamp. Gere entradas de log via Cloud Functions em escritas de documentos para garantir trilhas de auditoria completas mesmo se o código do cliente falhar.
Common mistake: Não tente armazenar logs como subcoleções sob usuários ou recursos — torna consultas entre usuários ou entre recursos impossíveis. Use uma coleção plana com campos de referência.
7. Geolocalização e Mapas
Serviços baseados em localização, apps de entrega, localizadores de lojas
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: Armazene strings geohash ao lado de campos GeoPoint para habilitar consultas de proximidade eficientes. O Firestore não pode fazer buscas nativas de raio, então geohash permite filtrar por prefixo para encontrar locais próximos. Use bibliotecas como geofire-common para gerar hashes.
Common mistake: Não tente consultar por intervalos de latitude/longitude diretamente — o Firestore não pode fazer consultas compostas de desigualdade. Sempre use prefixos geohash para filtragem baseada em localização.
8. Gestão de Inventário
Sistemas de armazém, rastreamento de estoque, apps de cadeia de suprimentos
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: Use transações ao atualizar quantity para prevenir condições de corrida. Para inventário de alto volume com atualizações frequentes, considere usar contadores distribuídos ou uma coleção de log de transações separada para evitar contenção de escrita.
Common mistake: Não incremente/decremente campos de quantity do código do cliente sem transações — você terá contagens incorretas sob acesso concorrente. Use Cloud Functions ou lógica server-side.
9. Feed Social e Seguidores
Redes sociais, feeds de notícias, streams de atividade
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: Armazene seguidores e seguindo como subcoleções separadas sob cada usuário para habilitar consultas eficientes em ambas as direções. Mantenha um likeCount em cache em posts atualizado via Cloud Functions em vez de ler toda a subcoleção de likes.
Common mistake: Não distribua posts para feeds de todos os seguidores na escrita — não escala. Em vez disso, consulte posts de usuários seguidos em tempo real usando uma consulta array-contains em IDs de following.
10. IoT e Dados de Sensores
Dashboards IoT, monitoramento ambiental, telemetria 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: Armazene dados de série temporal como subcoleções sob cada dispositivo. Para dados de alta frequência, agrupe escritas e implemente limpeza TTL via Cloud Functions para evitar crescimento ilimitado. Considere políticas TTL do Cloud Firestore ou exportações BigQuery para análises de longo prazo.
Common mistake: Não armazene cada leitura de sensor se seu dispositivo reporta a cada segundo — você excederá os limites de escrita e custos do Firestore. Agregue para intervalos de minuto ou hora, ou use Realtime Database para escritas de alta frequência.
Transforme Esses Padrões em Documentação
Esses JSON Schemas não são apenas exemplos — são documentação funcional que você pode usar no seu projeto. Salve-os como arquivos .schema.json no seu repositório, renderize-os com FireSchema, e dê à sua equipe uma referência navegável que eles realmente usarão. Leva 5 minutos para configurar.
Próximos Passos
Agora que você tem padrões para trabalhar, aprenda como usá-los efetivamente: