Modelagem de Dados
Como organizamos e estruturamos todos os dados do BITIO para que funcione de forma eficiente e segura.
O Grande Quadro
Imagine o banco de dados como uma biblioteca bem organizada:
- Cada restaurante tem sua própria seção (multi-tenant)
- Cada módulo (cardápio, reservas, etc.) tem suas próprias estantes (tabelas)
- Todos seguem o mesmo sistema de catalogação (padrões)
Princípios Simples
1. Isolamento por Restaurante
Dados do Restaurante A nunca se misturam com dados do Restaurante B
2. Um Lugar para Cada Coisa
Cada tipo de informação tem sua tabela específica
3. Ligações Inteligentes
Tabelas se conectam de forma lógica (cliente → pedido → produto)
4. Preparado para o Futuro
Estrutura suporta todos os 8 módulos desde o início
Como Nomeamos as Coisas
Para evitar confusão, usamos nomes consistentes em todo o sistema:
| Conceito Real | Nome na Tabela | Por que esse nome |
|---|---|---|
| Restaurante | tenants | Cada restaurante é um "inquilino" do sistema |
| Dono do restaurante | users | Pessoas que fazem login no painel |
| Cliente do restaurante | customers | Pessoas que usam o cardápio |
| Contato do cliente | customer_contacts | Telefones e emails em uma única tabela |
Entidades Principais - Explicação Simples
Tenants (Restaurantes)
O que é: Cada registro representa um restaurante no sistema
Por que existe: Mantém os dados de cada restaurante separados
CREATE TABLE tenants (
id UUID PRIMARY KEY,
name VARCHAR(255) NOT NULL, -- "Pizzaria do João"
slug VARCHAR(100) UNIQUE NOT NULL, -- "pizzaria-joao" (para URLs)
enabled_modules TEXT[], -- ['menu', 'reservations'] (módulos ativos)
status ENUM('active', 'inactive') DEFAULT 'active',
settings JSONB, -- Configurações específicas
created_at TIMESTAMP DEFAULT NOW()
);
Exemplo real:
- name: "Pizzaria do João"
- slug: "pizzaria-joao" → vira
pizzaria-joao.cardapio2biz.com.br - enabled_modules:
['menu', 'reservations']→ só esses módulos aparecem
Users (Usuários do Painel)
O que é: Pessoas que fazem login para gerenciar o restaurante
Tipos: Dono, gerente, ou equipe 2biz
CREATE TABLE users (
id UUID PRIMARY KEY,
tenant_id UUID REFERENCES tenants(id), -- A qual restaurante pertence
email VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
role ENUM('owner', 'manager', 'admin_2biz') NOT NULL,
active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT NOW()
);
Exemplo real:
- João (owner) → pode tudo na Pizzaria do João
- Maria (manager) → pode gerenciar, mas não alterar configurações
- Carlos (admin_2biz) → equipe 2biz, acesso a todos os restaurantes
Customers (Clientes dos Restaurantes)
O que é: Pessoas que usam o cardápio, fazem reservas, etc.
Importância: É a tabela mais importante, conecta todos os módulos
CREATE TABLE customers (
id UUID PRIMARY KEY,
tenant_id UUID REFERENCES tenants(id), -- A qual restaurante pertence
phone VARCHAR(20), -- Telefone principal
email VARCHAR(255), -- Email principal
name VARCHAR(255), -- Nome da pessoa
birth_date DATE, -- Para campanhas de aniversário
source ENUM('menu', 'reservation', 'manual'), -- Como chegou ao sistema
created_at TIMESTAMP DEFAULT NOW()
);
Exemplo real:
- Ana gerou um cupom no cardápio → source: 'menu'
- Depois fez uma reserva → mesmo registro, não duplica
- Sistema lembra dela em todas as interações
Módulos Específicos
Cardápio Digital
Categories: Organiza os produtos (Pizzas, Bebidas, Sobremesas)
CREATE TABLE categories (
id UUID PRIMARY KEY,
tenant_id UUID REFERENCES tenants(id),
name VARCHAR(255) NOT NULL, -- "Pizzas Doces"
sort_order INTEGER DEFAULT 0, -- Ordem no cardápio
active BOOLEAN DEFAULT true -- Pode desativar temporariamente
);
Products: Os itens do cardápio
CREATE TABLE products (
id UUID PRIMARY KEY,
tenant_id UUID REFERENCES tenants(id),
category_id UUID REFERENCES categories(id),
name VARCHAR(255) NOT NULL, -- "Pizza Margherita"
description TEXT, -- "Molho de tomate, mozzarela..."
price DECIMAL(10,2) NOT NULL, -- 25.90
photo_url VARCHAR(500), -- Link da foto
available BOOLEAN DEFAULT true -- Disponível hoje?
);
Coupons: Cupons de desconto gerados
CREATE TABLE coupons (
id UUID PRIMARY KEY,
tenant_id UUID REFERENCES tenants(id),
customer_id UUID REFERENCES customers(id),
code VARCHAR(50) UNIQUE NOT NULL, -- "PIZZA10"
type ENUM('percentage', 'fixed_amount'), -- 10% ou R$ 5,00
value DECIMAL(10,2) NOT NULL, -- 10 ou 5.00
used_count INTEGER DEFAULT 0, -- Quantas vezes foi usado
valid_until TIMESTAMP -- Data de expiração
);
Reservas
Tables: Mesas do restaurante
CREATE TABLE tables (
id UUID PRIMARY KEY,
tenant_id UUID REFERENCES tenants(id),
name VARCHAR(50) NOT NULL, -- "Mesa 1", "Varanda 3"
capacity INTEGER NOT NULL, -- Quantas pessoas cabem
active BOOLEAN DEFAULT true
);
Reservations: As reservas feitas
CREATE TABLE reservations (
id UUID PRIMARY KEY,
tenant_id UUID REFERENCES tenants(id),
customer_id UUID REFERENCES customers(id), -- Quem fez a reserva
table_id UUID REFERENCES tables(id), -- Em qual mesa
date DATE NOT NULL, -- Que dia
time TIME NOT NULL, -- Que hora
party_size INTEGER NOT NULL, -- Quantas pessoas
status ENUM('pending', 'confirmed', 'cancelled') DEFAULT 'pending',
notes TEXT -- Observações especiais
);
Como Tudo se Conecta
O diagrama mostra as conexões principais entre as tabelas:
erDiagram
TENANTS ||--o{ USERS : "possui"
TENANTS ||--o{ CUSTOMERS : "atende"
TENANTS ||--o{ CATEGORIES : "organiza"
TENANTS ||--o{ PRODUCTS : "vende"
TENANTS ||--o{ RESERVATIONS : "recebe"
CUSTOMERS ||--o{ COUPONS : "recebe"
CUSTOMERS ||--o{ RESERVATIONS : "faz"
CATEGORIES ||--o{ PRODUCTS : "agrupa"
TABLES ||--o{ RESERVATIONS : "hospeda"
CUSTOMERS {
uuid id PK
uuid tenant_id FK
string phone
string email
string name
date birth_date
}
PRODUCTS {
uuid id PK
uuid tenant_id FK
uuid category_id FK
string name
decimal price
boolean available
}
Fluxo de Dados Típico
- Cliente acessa cardápio → sistema identifica o restaurante pelo slug
- Cliente gera cupom → cria registro em
customersecoupons - Cliente faz reserva → usa mesmo
customer_id, criareservation - Restaurante vê dados → todos filtrados pelo
tenant_id
Índices Estratégicos
| Tabela | Coluna(s) | Tipo | Motivo |
|---|---|---|---|
customers | (tenant_id, phone) | UNIQUE | Busca rápida por telefone |
customers | (tenant_id, email) | UNIQUE | Busca por email |
products | (tenant_id, category_id) | B-tree | Filtro por categoria |
orders | (tenant_id, customer_id, order_date) | B-tree | Histórico do cliente |
reservations | (tenant_id, date, time) | B-tree | Agenda de reservas |
campaign_recipients | (campaign_id, customer_id) | UNIQUE | Evita envio duplicado |
Data Warehouse (S3 + Athena)
Tabelas para Exportação Diária
| Tabela | Frequência | Particionamento |
|---|---|---|
menu_views | Diária | year/month/day |
survey_responses | Diária | year/month/day |
reservations | Diária | year/month/day |
orders | Diária | year/month/day |
campaign_recipients | Diária | year/month/day |
Exemplo Athena Table
CREATE EXTERNAL TABLE bitio_analytics.orders (
id STRING,
tenant_id STRING,
customer_id STRING,
total_amount DOUBLE,
order_date TIMESTAMP
)
PARTITIONED BY (year INT, month INT, day INT)
STORED AS PARQUET
LOCATION 's3://bitio-analytics/orders/';
Implementação
Migration Strategy
- Prisma ORM para schema e migrations
- Multi-tenant setup com
tenant_idem todas as tabelas - Soft delete onde necessário (
deleted_at) - Row Level Security opcional no PostgreSQL
Performance
- Connection pooling via RDS Proxy quando escalar
- Read replicas para analytics (futuro)
- Particionamento das tabelas de logs/eventos (futuro)
Backup e Recovery
- Point-in-time recovery habilitado no RDS
- Backup diário automático
- Exportação para S3 como backup secundário
Esta modelagem garante que todos os módulos compartilhem uma base consistente, evitando retrabalho conforme novos recursos são adicionados.