Lógica de negócios em um banco de dados usando SchemaKeeper

O objetivo deste artigo é, usando a biblioteca schema-keeper mostrar ferramentas que permitem simplificar consideravelmente o processo de desenvolvimento de bancos de dados em projetos PHP que utilizam o SGBD PostgreSQL.

As informações deste artigo serão especialmente úteis para desenvolvedores que desejam aproveitar ao máximo as capacidades do PostgreSQL, mas enfrentam problemas de manutenção da lógica de negócios que foi delegada ao banco de dados.

O artigo não irá descrever as vantagens ou desvantagens de armazenar a lógica de negócios no banco de dados. Assume-se que a escolha já foi feita pelo leitor.

Serão discutidas as seguintes questões:

  1. De que forma armazenar o dump da estrutura do banco de dados no sistema de controle de versões (daqui em diante, VCS)
  2. Como acompanhar as alterações na estrutura do banco de dados após salvar o dump
  3. Como transferir alterações na estrutura do banco de dados para outros ambientes sem conflitos e arquivos gigantes de migração
  4. Como estabelecer o processo de trabalho paralelo em um projeto com vários desenvolvedores
  5. Como implantar com segurança um maior número de alterações na estrutura do banco de dados no ambiente de produção

    SchemaKeeper é projetado para trabalhar com procedimentos armazenados escritos na linguagem PL/pgSQL. Testes com outras linguagens não foram realizados, portanto, seu uso pode não ser tão eficaz ou pode ser impossível.

De que forma armazenar o dump da estrutura do banco de dados no VCS

A biblioteca schema-keeper oferece a função saveDump, que salva a estrutura de todos os objetos do banco de dados como arquivos de texto separados. O resultado é um diretório que contém a estrutura do banco de dados, dividida em arquivos agrupados que podem ser facilmente adicionados ao VCS.

Vamos considerar a conversão de objetos do banco de dados em arquivos com alguns exemplos:

Tipo de objeto
Esquema
Nome
Caminho relativo para o arquivo

Tabela
public
accounts
./public/tables/accounts.txt

Procedimento armazenado
public
auth(hash bigint)
./public/functions/auth(int8).sql

Visualização
booking
tariffs
./booking/views/tariffs.txt

O conteúdo dos arquivos é uma representação textual da estrutura de um determinado objeto do banco de dados. Por exemplo, para procedimentos armazenados, o conteúdo do arquivo será a definição completa do procedimento armazenado, começando com o bloco CREATE OR REPLACE FUNCTION.

Como pode ser visto na tabela acima, o caminho para o arquivo contém informações sobre o tipo, o esquema e o nome do objeto. Essa abordagem facilita a navegação pelo dump e a revisão do código das alterações no banco de dados.

Extensão .sql para arquivos com código de procedimentos armazenados, foi escolhido para que o IDE forneça automaticamente ferramentas para interagir com o banco de dados ao abrir o arquivo.

Como acompanhar as alterações na estrutura do banco de dados após salvar o dump

Ao salvar o dump da estrutura atual do banco de dados no VCS, obtemos a possibilidade de verificar se houve alterações na estrutura do banco após a criação do dump. Na biblioteca schema-keeper para identificar alterações na estrutura do banco de dados, existe a função verifyDump, que retorna informações sobre as diferenças sem efeitos colaterais.

Uma maneira alternativa de verificação é chamar novamente a função saveDump, especificando o mesmo diretório, e verificar no VCS a existência de alterações. Como todos os objetos do banco de dados são salvos em arquivos separados, o VCS mostrará apenas os objetos que foram alterados.
A principal desvantagem deste método é a necessidade de reescrever arquivos para ver as mudanças.

Como transferir alterações na estrutura do banco de dados para outros ambientes sem conflitos e arquivos gigantes de migração

Graças à função deployDump o código fonte de procedimentos armazenados pode ser editado exatamente da mesma forma que o código fonte normal do aplicativo. É possível adicionar/remover novas linhas no código dos procedimentos armazenados e imediatamente enviar as alterações para o sistema de controle de versões, ou criar/remover procedimentos armazenados criando/removendo os arquivos correspondentes no diretório do dump.

Por exemplo, para criar um novo procedimento armazenado no esquema public basta criar um novo arquivo com a extensão .sql no diretório public/functions, colocar nele o código fonte do procedimento armazenado, incluindo o bloco CREATE OR REPLACE FUNCTION, e em seguida chamar a função deployDump. De maneira semelhante, ocorre a alteração e exclusão de um procedimento armazenado. Assim, o código é enviado simultaneamente ao VCS e ao banco de dados.

Se ocorrer um erro no código fonte de algum procedimento armazenado, ou uma discrepância entre os nomes do arquivo e do procedimento armazenado, então deployDump não será executado, exibindo o texto do erro. A descoordenação dos procedimentos armazenados entre o dump e o banco de dados atual é impossível ao usar deployDump.

Ao criar um novo procedimento armazenado, não é necessário inserir manualmente o nome correto do arquivo. Basta que o arquivo tenha a extensão .sql. Após a chamada, deployDump o texto do erro conterá o nome correto, que pode ser usado para renomear o arquivo.

deployDump permite alterar os parâmetros da função ou o tipo de retorno sem ações adicionais, enquanto que com a abordagem clássica seria necessário
primeiro executar DROP FUNCTION, e somente depois CREATE OR REPLACE FUNCTION.

Infelizmente, existem algumas situações em que deployDump não é possível aplicar alterações automaticamente. Por exemplo, se uma função de gatilho que é utilizada por pelo menos um gatilho é excluída. Essas situações devem ser resolvidas manualmente usando arquivos de migração.

Se a transferência de mudanças em procedimentos armazenados é responsabilidade do próprio schema-keeper, então, para transferir as demais alterações na estrutura, é necessário utilizar arquivos de migração. Por exemplo, uma boa biblioteca para trabalhar com migrações é doctrine/migrations.

As migrações devem ser aplicadas antes do deployDump. Isso permite implementar todas as alterações na estrutura e resolver situações problemáticas, para que as mudanças nos procedimentos armazenados sejam transferidas sem problemas posteriormente.

O trabalho com migrações será descrito em mais detalhes nas seções seguintes.

Como estabelecer o processo de trabalho paralelo em um projeto com vários desenvolvedores

É necessário criar um script de inicialização completa do BD, que será executado pelo desenvolvedor em sua máquina de trabalho, alinhando a estrutura do BD local ao dump salvo no VCS. É mais fácil dividir a inicialização do BD local em 3 etapas:

  1. Importar o arquivo com a estrutura base, que será chamado, por exemplo, base.sql
  2. Aplicação das migrações
  3. Chamada deployDump

base.sql — é o ponto de partida sobre o qual as migrações são aplicadas e é executado deployDump, ou seja, base.sql + migrações + deployDump = estrutura atual do BD. Um arquivo assim pode ser gerado com a ajuda da ferramenta pg_dump. É usado base.sql exclusivamente ao inicializar o banco de dados do zero.

Vamos chamar o script de inicialização completa do BD refresh.sh. O fluxo de trabalho pode ser assim:

  1. O desenvolvedor executa em seu ambiente refresh.sh e obtém a estrutura atual do BD
  2. O desenvolvedor começa a trabalhar na tarefa atribuída, modificando o BD local para as necessidades da nova funcionalidade (ALTER TABLE ... ADD COLUMN etc.)
  3. Após a conclusão da tarefa, o desenvolvedor chama a função saveDump, para registrar no VCS as alterações feitas no BD
  4. O desenvolvedor executa novamente refresh.sh, então verifyDump, que agora mostra a lista de alterações para inclusão na migração
  5. O desenvolvedor transfere todas as mudanças de estrutura para o arquivo de migração, executa novamente refresh.sh e verifyDump, e, se a migração estiver correta, verifyDump mostrará que não há diferenças entre o BD local e o dump salvo.

O processo descrito acima é compatível com os princípios do gitflow. Cada branch no VCS conterá sua própria versão do dump, e ao mesclar branches acontecerá a fusão dos dumps. Na maioria dos casos, após a fusão, não é necessário realizar nenhuma ação adicional, mas se alterações foram feitas em diferentes branches, por exemplo, na mesma tabela, um conflito pode ocorrer.

Consideremos uma situação de conflito com o seguinte exemplo: há uma branch develop, da qual duas branches foram criadas: feature1 e feature2, que não têm conflitos com develop, mas têm conflitos entre si. O objetivo é mesclar ambas as branches em develop. Para esse caso, recomenda-se primeiro mesclar uma das branches em develop, e depois mesclar develop na branch restante, resolvendo os conflitos na branch restante, após o que a última branch pode ser mesclada em develop. No estágio de resolução de conflitos, pode ser necessário corrigir o arquivo de migração na última branch, para que ele corresponda ao dump final, que inclui os resultados das fusões.

Como implantar com segurança um maior número de alterações na estrutura do banco de dados no ambiente de produção

Graças à presença de um dump da estrutura atual do banco de dados no VCS, é possível verificar o banco de produção em relação à estrutura exigida. Isso garante que todas as alterações que os desenvolvedores tinham em mente foram transferidas com sucesso para o banco de produção.

Uma vez que DDL no PostgreSQL é transacional, recomenda-se seguir a seguinte ordem de deploy, para que, em caso de erro inesperado, possa ser executado um ROLLBACK:

  1. Iniciar transação
  2. Executar todas as migrações na transação
  3. Na mesma transação, executar deployDump
  4. . Sem encerrar a transação, executar verifyDump. Se não houver erros, executar COMMIT. Se houver erros, executar ROLLBACK

Esses passos se integram facilmente nas abordagens existentes de deploy de aplicações, incluindo zero-downtime.

Conclusão

Graças aos métodos descritos acima, é possível extrair o máximo de desempenho de projetos "PHP + PostgreSQL", sacrificando uma quantidade relativamente pequena de conforto de desenvolvimento em comparação com a implementação de toda a lógica de negócios no código principal da aplicação. Além disso, o processamento de dados em PL/pgSQL geralmente parece mais transparente e requer menos código do que a mesma funcionalidade escrita em PHP.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster