Planear uma base de dados MySQL para aplicações web

Quando três funcionários registam mercadoria em paralelo de manhã, um cliente verifica o estado da entrega, e o back office cria uma fatura, a qualidade de uma aplicação não se mostra no seu design. Demonstra-se em saber se todos veem exatamente o mesmo estado de dados correto. Planear uma base de dados MySQL para uma aplicação web não significa, por isso, criar tabelas o mais rapidamente possível. Significa compreender os fluxos de trabalho reais com precisão suficiente para garantir que os dados permanecem fiáveis mesmo sob carga, durante erros, e à medida que o negócio cresce.

Especialmente em plataformas internas, processos de armazém e encomendas, ou portais voltados para o cliente, a base de dados é frequentemente tratada demasiado tarde. Primeiro a interface é construída, depois são adicionados campos, seguidos de exceções. Isso funciona para um protótipo. Nas operações, isto resulta em conjuntos de dados duplicados, estados pouco claros, e relatórios em que já ninguém confia totalmente.

Planear uma base de dados MySQL para aplicações web: comece pelo fluxo de trabalho

O primeiro esboço não deveria começar com nomes de colunas, mas com uma situação de trabalho concreta. Tome uma receção de mercadoria: uma entrega chega, é atribuída a um fornecedor e a uma encomenda, as quantidades são verificadas, uma localização de armazenamento é atribuída, e o inventário muda. Dependendo da operação, este processo requer adicionalmente fotografias, uma inspeção de qualidade, um estado de retenção, ou uma correção rastreável. Deste fluxo de trabalho emergem os objetos funcionais. Exemplos típicos são artigos, fornecedores, encomendas, posições, localizações de armazenamento, movimentos de inventário, e utilizadores.

A distinção entre um objeto e um evento é crucial. Um artigo descreve o que algo é. Um movimento de inventário documenta que uma quantidade mudou numa localização específica num momento específico. Misturar ambos numa única tabela leva rapidamente a uma perda de rastreabilidade.

Algumas perguntas difíceis ajudam para cada objeto: qual é a identidade única? Que informação pode mudar? Quem tem permissão para a mudar? Que dados têm de ser retidos historicamente? E que regras se aplicam quando duas pessoas trabalham simultaneamente? Estas perguntas previnem a improvisação subsequente melhor do que uma longa lista de campos de base de dados supostamente completos.

O modelo de dados deveria expressar regras

Uma base de dados não é meramente armazenamento para entradas de formulário. Deveria fazer cumprir as regras centrais por si mesma. Se cada movimento de inventário tiver de pertencer exatamente a um artigo e uma localização de armazenamento, as chaves estrangeiras pertencem ao modelo. Se um número de encomenda externo só puder ocorrer uma vez por inquilino, é necessário um índice único. Se uma posição nunca devesse existir sem uma encomenda-cabeçalho, esta relação tem de ser modelada claramente.

O MySQL 8 com InnoDB fornece bases robustas para isto: transações, chaves estrangeiras, mecanismos de bloqueio, e alterações consistentes através de várias tabelas. Ao escrever um movimento, inventário atual, e registo de inspeção durante um registo de receção de mercadoria, isto deveria acontecer como uma transação coesa. Se um passo falhar, nenhuma operação meio-terminada pode permanecer.

No entanto, nem toda a regra pertence à base de dados. As aprovações, lógica de preços complexa, ou passos de processo dependentes de função são frequentemente melhor colocados na lógica da aplicação porque mudam funcionalmente mais depressa. O limite é pragmático: as regras cuja violação danifica permanentemente os dados deveriam ser protegidas o mais perto possível dos dados. As regras que mudam frequentemente ou dependem fortemente do contexto requerem código de aplicação bem testado.

Não confunda o histórico com os valores atuais

Um erro comum é armazenar apenas o inventário atual ou o estado atual. Isso é suficiente até alguém perguntar porque é que a quantidade mudou ontem ou quem reiniciou uma encomenda. Para sistemas operacionais, um histórico de movimentos ou eventos é frequentemente mais valioso do que um único campo sobrescrevível.

Isto não significa registar permanentemente cada movimento de clique. As alterações relevantes para o negócio deveriam ser registadas: alterações de estado, modificações de quantidade, correções, aprovações, e atribuições. Uma boa entrada de auditoria contém um registo de tempo, utilizador ou processo do sistema, valor anterior e novo, e uma razão compreensível quando o fluxo de trabalho o exige. Isto torna possível esclarecer erros sem ter de procurar em e-mails, listas em papel, ou cópias de segurança da base de dados.

Escolha chaves, tipos de dados, e convenções de nomenclatura de forma consciente

As decisões técnicas parecem pequenas, mas moldam a manutenção e integrações ao longo dos anos. Para chaves primárias internas, os valores BIGINT com atribuição automática são frequentemente uma escolha sóbria e facilmente gerível. Os UUIDs podem ser sensatos quando os dados se originam offline, vários sistemas escrevem de forma independente, ou as interfaces externas não deveriam expor IDs sequenciais. No entanto, custam mais armazenamento e requerem um pouco mais de atenção com índices e ordenação.

Os valores monetários devem ser armazenados como DECIMAL, não FLOAT ou DOUBLE. As quantidades também precisam de uma precisão funcionalmente apropriada: as contagens de itens são frequentemente números inteiros, enquanto os pesos e comprimentos não são. Os registos de tempo deveriam ser tratados de forma uniforme, idealmente internamente em UTC, enquanto a interface exibe o fuso horário local da operação. Especialmente durante mudanças de turno e horário de verão, isto previne discrepâncias difíceis de encontrar.

Os nomes também deveriam ser aborrecidos e inequívocos. order_items ou inventory_movements são mais úteis do que abreviaturas criativas que só a equipa de projeto original entende. Formas singulares ou plurais consistentes são menos importantes do que a consistência. Igualmente sensatos são campos como created_at, updated_at, e, quando necessário, deleted_at. Uma eliminação suave não é, no entanto, uma obrigação padrão. Para registos legalmente ou operacionalmente relevantes, um cancelamento limpo é geralmente melhor do que um conjunto de dados invisivelmente eliminado.

Os índices seguem as consultas reais, não a adivinhação

Um índice pode acelerar massivamente uma pesquisa, mas torna as operações de escrita mais complexas e consome armazenamento. Por isso, "um índice em cada campo" não é uma estratégia. As consultas mais importantes deveriam ser estabelecidas cedo: encomendas em aberto de um cliente, movimentos de um artigo dentro de um período, inventário por localização de armazenamento, ou registos recentemente modificados para uma interface.

A ordem dos índices compostos importa aqui. Se a aplicação pesquisa regularmente por tenant_id, status, e created_at, um índice composto exatamente nesta ordem é frequentemente sensato. Se realmente se ajusta é mostrado pelo plano de execução usando EXPLAIN, não por intuição. As bases de dados não se tornam rápidas por truques espetaculares, mas por consultas observáveis, índices correspondentes, e volumes de dados testados realisticamente.

Para tabelas em crescimento, uma estratégia de retenção clara vale a pena. Os registos técnicos precisam de estar na base de dados de produção primária durante cinco anos? Não necessariamente. Os registos de negócio, movimentos, e provas de inspeção requerem períodos de retenção diferentes dos da informação de depuração. O arquivamento não é sinal de um sistema fraco, mas uma decisão operacional deliberada.

A operação multiutilizador requer transações e estados claros

Numa aplicação web, vários pedidos acedem aos mesmos dados simultaneamente. Isto é normal nas operações diárias de armazém, não uma exceção. Dois funcionários podem registar o mesmo inventário enquanto uma importação cria novas encomendas. Sem transações e bloqueio direcionado, existe o risco de modificações perdidas ou inventários negativos que só se tornam aparentes semanas depois.

Para operações críticas, deveria ser claro que dados são lidos e escritos dentro de uma transação. Por vezes uma atualização atómica é suficiente, tal como um inventário que só é alterado se a quantidade disponível for suficiente. Noutros casos, um bloqueio de linha é sensato para que uma operação possa verificar o estado dos dados de forma controlada e modificá-lo depois. As transações longas, por outro lado, são problemáticas: bloqueiam outro trabalho e aumentam o risco de conflitos.

Igualmente importante é um conjunto limitado de estados funcionais. Uma encomenda não deveria ser "aberta", "parcialmente entregue", e "processada manualmente" simultaneamente devido a campos conflituantes serem mantidos. As transições de estado definidas tornam as interfaces, relatórios, e automações mais simples. As exceções podem ser permitidas, mas deveriam ser nomeadas e documentadas.

Planeie a segurança, inquilinos, e operações desde o início

A aplicação deveria usar um utilizador de base de dados dedicado para o MySQL com privilégios mínimos. O acesso de escrita para a aplicação web não significa que este utilizador precise de eliminar tabelas ou alterar privilégios de utilizador. As contas administrativas não pertencem a ficheiros de configuração de produção e nunca a um repositório.

Quando vários clientes, localizações, ou empresas trabalham dentro de uma aplicação, o isolamento de inquilinos é uma decisão arquitetónica, não uma condição de filtro retroativa. Uma base de dados partilhada com um tenant_id pode ser eficiente e facilmente gerível, mas exige verificações consistentes em cada consulta e regras claras para índices. As bases de dados separadas oferecem isolamento mais forte, mas aumentam o esforço em atualizações, avaliações, e operações. Que variante se ajusta depende dos requisitos de privacidade de dados, volume de dados, e modelo de negócio.

As cópias de segurança só são cópias de segurança depois de uma restauração ter sido testada. É necessário um ritmo definido para cópias de segurança, retenção, e recuperação. Da mesma forma, a monitorização do espaço de armazenamento, consultas lentas, e tarefas falhadas, juntamente com atualizações documentadas, pertencem ao sistema. O MySQL 8, PHP 8.4, e aplicações web modernas podem ser bem operados a longo prazo se as dependências, credenciais de acesso, e passos de implementação não residirem apenas na cabeça de um programador.

Um plano sensato antes do primeiro dia em produção

Antes da implementação, deveria existir um modelo de dados compacto com fluxos de trabalho de exemplo. Isto inclui tabelas e relações chave, regras de estado, permissões, consultas esperadas, interfaces, e um conceito para cópias de segurança e registos de auditoria. Este plano não precisa de ter cem páginas. Tem de capturar decisões que mais tarde seriam dispendiosas de corrigir.

Na softify.pro, o planeamento de bases de dados começa, por isso, com as pessoas que registam, verificam, fazem picking, ou resolvem exceções. Se uma folha de cálculo existente mapeia de forma fiável um processo gerível, pode continuar a ser a solução correta. Se várias pessoas trabalham simultaneamente, surgem registos, e os erros têm de ser rastreáveis, a base de dados merece, por outro lado, o mesmo esforço de planeamento que a interface. A melhor arquitetura, no final, é aquela que simplifica o dia de trabalho e ainda pode ser alterada de forma transparente daqui a dois anos.