Modelagem de dados para penteado cabelo longo Liberdade salão de beleza feminino São Paulo

Aprenda a modelar banco de dados para salão de beleza focado em penteados de cabelo longo na Liberdade, São Paulo. Consultoria MySQL especializada.

Mariana, gestora de um salão de beleza feminino na Liberdade, região central de São Paulo, enfrentava um problema recorrente: agendamentos duplicados para penteados de cabelo longo nos horários de pico de sexta-feira. O sistema antigo, baseado em planilhas, não impedia que duas clientes fossem marcadas com a mesma profissional no mesmo intervalo. Além disso, o histórico de preferências — como alergia a determinados fixadores ou preferência por tranças embutidas — ficava perdido em anotações de papel. A consequência direta era retrabalho, insatisfação das clientes e perda de receita em dias de maior movimento.

A solução passou por uma modelagem de dados pensada para o domínio específico: penteados em cabelo longo, com variações de técnica (coque alto, trança lateral, semipreso com torções), duração variável (45 a 120 minutos) e consumo de produtos controlado por atendimento. Neste artigo, detalhamos a arquitetura relacional que resolve esses desafios, aplicando princípios de normalização, integridade e performance no MySQL 8.

Entidades centrais e seus relacionamentos

O modelo começa com cinco tabelas principais: cliente, profissional, servico, agendamento e produto. A tabela cliente armazena dados cadastrais (nome, telefone, CPF, email) e campos específicos do domínio: tipo_cabelo (enum: ’liso’, ‘ondulado’, ‘cacheado’, ‘crespo’), comprimento (enum: ’longo’, ‘medio’, ‘curto’), alergias_produtos (text) e observacoes_tecnicas (text). A tabela profissional inclui especialidades (json array com técnicas dominadas), carga_horaria_semanal (json com dias e horários) e nivel_experiencia (enum: ‘junior’, ‘pleno’, ‘senior’).

A tabela servico é o catálogo de penteados oferecidos. Campos essenciais: nome (ex: ‘Coque bailarina com trança’), descricao_tecnica, duracao_minima_min, duracao_maxima_min, preco_base, complexidade (1 a 5), ativo (boolean), versao (int), vigencia_inicio (date), vigencia_fim (date). A versionação permite alterar preço ou descrição sem perder o histórico do que foi cobrado em cada atendimento. Para atributos variáveis por serviço — como produtos obrigatórios, acessórios necessários ou etapas do passo a passo — criamos servico_detalhe com servico_id, chave (ex: ‘produto_fixador’, ‘acessorio_tiara’), valor (text) e ordem.

O coração operacional é agendamento: cliente_id, profissional_id, servico_id, data_hora_inicio, data_hora_fim, status (enum: ‘agendado’, ‘confirmado’, ’em_andamento’, ‘concluido’, ‘cancelado’, ’nao_compareceu’), valor_cobrado, observacoes_profissional. Uma constraint única composta (profissional_id, data_hora_inicio, data_hora_fim) com WHERE status NOT IN ('cancelado', 'nao_compareceu') impede sobreposições reais. Um trigger BEFORE INSERT valida também se o profissional está escalado naquele horário conforme sua carga_horaria_semanal.

Para rastrear produtos consumidos em cada atendimento, a tabela agendamento_produto registra agendamento_id, produto_id, quantidade_ml, custo_unitario. Isso viabiliza relatórios de custo por serviço e reposição de estoque baseada em consumo real.

Normalização avançada para atributos variáveis de penteado

Penteados de cabelo longo têm atributos que não cabem em colunas fixas: uma cliente pode querer trança embutida com fitas, outra coque com flores naturais, outra semipreso com grampos decorativos. Em vez de criar dezenas de colunas esparsas, adotamos o padrão EAV (Entity-Attribute-Value) controlado na tabela servico_detalhe e sua extensão agendamento_servico_detalhe para personalização por atendimento.

Exemplo prático: o serviço “Trança lateral com fitas” tem em servico_detalhe registros com chaves tecnica_base=‘trança_lateral’, fita_cor_padrao=‘dourado’, tempo_adicional_min=‘15’. No agendamento específico, agendamento_servico_detalhe pode sobrescrever fita_cor=‘rosa’ e adicionar flores_naturais=‘sim’. Essa flexibilidade mantém o esquema limpo e permite consultas como “quantos penteados usaram fitas no último mês” com um simples GROUP BY na chave.

A normalização vai até a 3FN para tabelas transacionais e 4FN para catálogos, evitando anomalias de atualização. Por exemplo, produto tem fornecedor_id referenciando fornecedor, não o nome do fornecedor repetido. Já profissional_especialidade (N:N) separa especialidades do profissional, permitindo que uma profissional tenha múltiplas técnicas e uma técnica seja dominada por vários profissionais.

Índices, particionamento e performance para consultas de agenda

A tabela agendamento é a mais consultada: listagem do dia por profissional, busca por cliente, relatórios de ocupação semanal. Índices estratégicos:

CREATE INDEX idx_agendamento_prof_data ON agendamento (profissional_id, data_hora_inicio);
CREATE INDEX idx_agendamento_cliente_data ON agendamento (cliente_id, data_hora_inicio);
CREATE INDEX idx_agendamento_status_data ON agendamento (status, data_hora_inicio);
CREATE INDEX idx_agendamento_data_status ON agendamento (data_hora_inicio, status);

Para volumes acima de 100 mil agendamentos/ano, particionamos por RANGE COLUMNS (data_hora_inicio) com partições mensais. O EXPLAIN de consultas de agenda diária mostra partitions: p2024_01 — leitura apenas da partição do mês. Veja como interpretar planos de execução em como ler EXPLAIN MySQL 8.

A tabela cliente recebe índice único em cpf e índice composto (tipo_cabelo, comprimento) para campanhas segmentadas (ex: “clientes com cabelo longo cacheado que não vêm há 60 dias”). A tabela servico tem índice em (ativo, complexidade) para listar opções disponíveis ordenadas por dificuldade.

Integridade transacional e concorrência em agendamentos simultâneos

Quando duas recepcionistas tentam agendar o mesmo horário para a mesma profissional, o banco deve garantir atomicidade. Usamos SELECT ... FOR UPDATE na transação de agendamento:

START TRANSACTION;
SELECT * FROM agendamento 
WHERE profissional_id = ? AND data_hora_inicio < ? AND data_hora_fim > ?
AND status NOT IN ('cancelado', 'nao_compareceu')
FOR UPDATE;
-- se linha retornada, ROLLBACK e erro de conflito
-- senão, INSERT agendamento + COMMIT

O nível de isolamento REPEATABLE READ (padrão do InnoDB) garante que a leitura do intervalo seja consistente até o commit. Para picos extremos (Black Friday, Dia das Mães), consideramos SELECT ... FOR UPDATE SKIP LOCKED com retentativa, mas o padrão acima atende 99% dos cenários de salão.

A integridade referencial usa ON DELETE RESTRICT em todas as FKs: não se apaga cliente com agendamentos, nem serviço com histórico. Para “excluir” logicamente, usamos ativo = FALSE ou status = 'cancelado'. Auditoria de alterações críticas (preço, status, profissional) é feita via trigger AFTER UPDATE em agendamento_auditoria com usuario_sistema, timestamp, campo_alterado, valor_antigo, valor_novo.

Relatórios gerenciais e consultas analíticas suportadas pelo modelo

O modelo viabiliza dashboards sem queries complexas:

  • Ocupação por profissional/semana: SELECT profissional_id, WEEK(data_hora_inicio) AS semana, SUM(TIMESTAMPDIFF(MINUTE, data_hora_inicio, data_hora_fim))/60 AS horas_ocupadas FROM agendamento WHERE status IN ('concluido','em_andamento') GROUP BY profissional_id, semana
  • Faturamento por tipo de penteado: join agendamento + servico + agendamento_produto para receita líquida (valor_cobrado - custo_produtos)
  • Produtos mais usados em cabelo longo: SELECT p.nome, SUM(ap.quantidade_ml) FROM agendamento_produto ap JOIN agendamento a ON ... JOIN servico s ON ... JOIN produto p ON ... WHERE s.comprimento_alvo = 'longo' GROUP BY p.nome ORDER BY 2 DESC
  • Clientes inativos por perfil: SELECT c.* FROM cliente c LEFT JOIN agendamento a ON c.id = a.cliente_id AND a.data_hora_inicio > DATE_SUB(NOW(), INTERVAL 90 DAY) WHERE a.id IS NULL AND c.comprimento = 'longo'

Essas consultas rodam em milissegundos com os índices propostos. Para análises históricas pesadas, recomendamos réplica de leitura ou materialized views refrescadas noturnamente — tema abordado em boas práticas em arquitetura de banco de dados.

Evolução do schema e migrações versionadas

Todo script de alteração (DDL) vive em controle de versão com nomenclatura V{versao}__{descricao}.sql (ex: V12__add_fita_cor_to_servico_detalhe.sql). Ferramentas como Flyway ou Liquibase aplicam em ordem, registrando em tabela schema_version. Cada migração é testada em staging com carga de produção anonimizada antes do deploy.

Adições de colunas NULL sem DEFAULT são instantâneas no MySQL 8 (Instant DDL). Índices novos usam ALGORITHM=INPLACE, LOCK=NONE quando possível. Remoção de colunas ou mudança de tipo exigem janela de manutenção ou pt-online-schema-change. Documentamos no dicionário de dados: tabela, coluna, tipo, nulabilidade, default, FK, índice, regra de negócio, exemplo de valor.

A modelagem também prevê expansão: se o salão abrir filial, adicionamos unidade_id em profissional, agendamento, estoque_produto com FK para nova tabela unidade. O particionamento de agendamento pode incluir unidade_id como subpartição HASH.

Para cenários de backup e recuperação desse modelo transacional, consulte backup automatizado para salão de cabeleireiro Perdizes.

Considerações finais

Uma modelagem bem feita para salão de beleza não é apenas “tabelas e relacionamentos” — é traduzir a operação real (penteados longos, profissionais com especialidades, produtos consumidos, horários disputados) em estruturas que o MySQL otimize nativamente. Normalização controlada, índices cirúrgicos, particionamento por tempo, integridade transacional e migrações versionadas formam a base que permite ao salão da Liberdade escalar de 50 para 500 agendamentos/semana sem reescrever o sistema. O próximo passo é instrumentar métricas de query latency e lock contention para ajustar índices conforme o uso real — e, quando a demanda exigir, avaliar consultoria DBA para ambientes MySQL críticos.

Ao aplicar esses princípios, o salão transforma dados em decisões: sabe qual penteado dá mais lucro, qual profissional tem agenda ociosa, qual produto vai faltar na terça-feira. E a Mariana deixa de apagar incêndios para focar no que importa: a experiência da cliente na cadeira.

Checklist prático

0 de 9 concluídos

Perguntas frequentes

Quais são as principais entidades para modelar agendamentos de penteados em um salão?

As entidades essenciais incluem Cliente, Profissional, Serviço (penteado), Agendamento, ProdutoUtilizado e HistóricoAtendimento. Cada uma captura atributos específicos como tipo de cabelo, duração do serviço e produtos aplicados, garantindo rastreabilidade completa.

Como normalizar a tabela de serviços para diferentes tipos de penteado em cabelo longo?

Crie uma tabela Servico com campos genéricos (nome, duração estimada, preço base) e uma tabela associativa ServicoDetalhe para atributos variáveis como técnica (trança, coque, semipreso), nível de complexidade e produtos necessários. Isso evita colunas esparsas e facilita consultas.

Qual a melhor forma de armazenar preferências de clientes para penteados recorrentes?

Utilize uma tabela PreferenciaCliente com chaves estrangeiras para Cliente e Serviço, adicionando campos como frequência_semanal, observacoes_tecnicas e produtos_alergicos. Índices compostos em (cliente_id, servico_id) aceleram buscas por histórico de preferências.

Como lidar com conflitos de horário entre profissionais na mesma faixa de atendimento?

Implemente constraints de unicidade composta em Agendamento (profissional_id, data_hora_inicio, data_hora_fim) e use triggers BEFORE INSERT para validar sobreposição. Para escalabilidade, considere particionamento por data na tabela de agendamentos.

É necessário versionar alterações no catálogo de penteados oferecidos?

Sim. Adicione campos version, data_vigencia_inicio e data_vigencia_fim na tabela Servico, mantendo histórico de preços e descrições. Isso permite relatórios precisos de faturamento por período e auditoria de alterações no portfólio.

Do corte ao penteado de festa, penteado para festa em Pinheiros reúne cabeleireiros e tratamentos cap femininos na capital.