Bancos de dados relacionais e desenvolvimento iterativo

19

Em muitas abordagens ao desenvolvimento de software, como metodologias ágeis, Design Orientado a Domínio e Análise e Design Orientado a Objetos, somos incentivados a adotar uma abordagem iterativa ao desenvolvimento.

Portanto, não devemos executar nosso modelo de domínio corretamente na primeira vez em que começarmos a trabalhar no projeto. Em vez disso, à medida que o tempo passa, refatoramos o modelo porque adquirimos uma compreensão mais profunda do domínio do problema com o tempo.

Além disso, mesmo se tentarmos antecipar um modelo perfeito, o que já estou convencido de que é muito difícil, os requisitos podem mudar. Então, depois que o software tenha sido implantado para produção, os usuários finais podem notar que uma certa exigência não foi completamente compreendido, ou pior, alguma exigência estava faltando.

O ponto aqui é que podemos acabar precisando alterar o modelo após a implantação do software. Se isso acontecer, temos um problema: o banco de dados de produção possui dados do usuário, o que é importante e já está ajustado no formato do modelo antigo .

Atualizar o código pode ser uma tarefa difícil se o código não for bem projetado e se o sistema for grande. Mas isso pode ser feito com o tempo, temos ferramentas como o Git que nos ajudam a fazer isso sem danificar a versão pronta para produção.

Por outro lado, se o modelo for alterado, se as propriedades das classes desaparecerem ou o que for, o banco de dados também deve ser alterado. Mas temos um problema: já existem dados que não podem ser perdidos, que já estão formatados para o modelo antigo.

Parece que um banco de dados relacional aqui está sendo uma barreira nos impedindo de desenvolver iterativamente e até atualizar o software quando exigido pelos usuários finais.

Uma abordagem que eu já usei foi codificar uma classe especial que mapeia tabelas de banco de dados antigas para novas. Portanto, essas classes selecionam dados no formato antigo, convertem-no para o formato usado pelo novo modelo e salvam nas novas tabelas.

Essa abordagem parece não ser a melhor. Minha pergunta aqui é: existem abordagens conhecidas e recomendadas para conciliar desenvolvimento iterativo com bancos de dados relacionais?

user1620696
fonte
6
Aliás, acho que isso não tem nada a ver com bancos de dados relacionais em particular. Eu tenho um problema semelhante com um projeto no qual estou trabalhando, mas o temos com o esquema para nossas seqüências JSON que representam objetos muito não relacionais. Provavelmente afeta todas as formas de persistência igualmente.
Ixrec
1
Você altera o esquema do banco de dados de maneira a não perder dados, en.wikipedia.org/wiki/Schema_migration .
RemcoGerlich 21/02
1
Tenho certeza de que esse tópico foi discutido extensivamente em algum lugar antes, mas não é possível encontrá-lo nos programadores. Mas veja aqui martinfowler.com/articles/evodb.html ou aqui stackoverflow.com/questions/334059/…
Doc Brown
1
"Além disso, mesmo se tentarmos antecipar um modelo perfeito, o que já estou convencido de que é muito difícil, os requisitos podem mudar." Gostaria de acrescentar que você nem deve tentar obter um modelo (quase perfeito) na frente. Isso pode vincular sua mentalidade a um tipo de solução, em vez de manter suas opções em aberto.
curvada

Respostas:

15

Ele não precisa ser de classes especiais, mas sim, você precisa de algo que pegue o banco de dados no formato anterior e o converta no atual.

A questão aqui é que você precisa desenvolver um processo para escrever e testar esses scripts e disciplina para nunca tocar os bancos de dados de teste e produção manualmente, mas sempre usando scripts de migração.

Toda vez que você precisa fazer uma alteração no banco de dados, escreve um script que o fará, seja em SQL ou usando sua camada ORM, e o compromete com o controle de versão, juntamente com as alterações que requerem o novo esquema. Então você tem algum script de controle que atualizará o banco de dados aplicando todos os scripts de migração que ainda não foram aplicados em uma sequência.

E certifique-se de modificar apenas os ambientes de desenvolvimento, teste e controle de qualidade compartilhados aplicando os scripts e revertendo para a versão anterior, se eles não funcionarem, para ter certeza de que eles funcionarão como planejado quando você os liberar na produção .

A nova instalação é feita simplesmente aplicando todos os scripts. Depois de um tempo, você poderá ter centenas deles e achar que é muito ineficiente, mas não caia na armadilha de tentar otimizá-lo. A instalação é uma atividade única e mantém trunfos confiáveis, tornando-o rápido.

O @Doc Brown já vinculou Martin Fowler: Evolutionary Database Design e /programming/334059/agile-development-and-database-changes , e eu adicionaria Alex Papadimoulis: Database Changes Done Right , que é mais curto e tem alguns exemplos.

Como um exemplo decente de ferramenta para implementar esse processo, sugiro Alambique . Ele é baseado na estrutura Python SQLAlchemy , mas você pode usá-lo com outras linguagens e estruturas se elas não tiverem seu próprio suporte à migração. A página da Wikipedia sobre Migração de esquema lista mais dessas ferramentas .

Jan Hudec
fonte
1
@Tibo, você cria o esquema do zero executando a mesma sequência de scripts. É assim que você gerencia o problema. Como padrão, você pode passar de qualquer instância do banco de dados - incluindo uma que ainda não existe - para um esquema atual e ter a confiança de que é o mesmo. Não há necessidade de ter duas maneiras, como no seu exemplo. (Pelo menos não dada uma linha de base consistente - o primeiro passo é estabelecer a linha de base e uma vez que você chegar a essa linha de base o problema desaparece.)
Murph
1
Polegares para cima para o artigo de Alex; pode não ser mais curto, mas faz uma leitura muito mais orientada para a prática e divertida.
Murphy
1
Somos uma loja Agile e executamos um serviço de tempo de atividade 100%, e ambos se aplicam ao DB também. Migramos o esquema de produção, em média, uma vez por dia, e eu recomendaria tudo o que Jan disse. Uma coisa adicional que fazemos que é inestimável é o que chamamos de teste de migração, que é executado como parte de nosso processo de criação e implantação. Ele tira uma captura instantânea de esquema da produção, aplica todas as migrações pendentes do mestre para ela e executa os testes de unidade do código de produção atualmente implantado nesse esquema. O objetivo é verificar se a aplicação das migrações não interromperá o sistema em execução.
Gordon Wrigley
1

Curiosamente, esse é o mesmo problema enfrentado pela minha equipe de desenvolvimento atual. A pergunta contém várias sub-perguntas, portanto elas serão abordadas de forma independente.

Em primeiro lugar, um banco de dados relacional restringe demais o modelo de dados, dificultando muito as alterações?

Certamente , mas não necessariamente pelas razões citadas. Infelizmente, a versatilidade dos sistemas de gerenciamento de banco de dados relacional também leva à sua queda. O RDBMS foi desenvolvido originalmente para oferecer uma plataforma de armazenamento de dados relativamente simples que aceitaria grandes conjuntos de dados e os reduziria a um tamanho relativamente pequeno. Isso foi feito à custa da complexidade do modelo de dados e do poder de computação necessário. À medida que a complexidade do banco de dados aumentava, surgiram procedimentos, visualizações, funções e gatilhos armazenados para ajudar os administradores de banco de dados a lidar com a complexidade de maneira consistente e escalável.

Infelizmente, o modelo de banco de dados relacional não é orientado a objetos e, naturalmente, não é mapeado para entidades do mundo real como um modelo de dados. Isso nos leva à necessidade de ferramentas intermediárias, como mapeadores objeto-relacionais e similares. Infelizmente, embora essas ferramentas claramente tenham um lugar no mundo de desenvolvimento atual, seu uso é direcionado apenas a um sintoma do problema de complexidade de dados relacionais, e não à causa subjacente, que é um desalinhamento do modelo de dados para o mundo real.

Isso leva à segunda parte da pergunta, que era realmente mais uma suposição, mas deveria ser vista como uma pergunta: devemos executar nosso modelo de domínio corretamente da primeira vez?

Sim, até certo ponto. Como a pergunta apontou, raramente é possível entender completamente o problema quando iniciamos o processo de design. No entanto, a diferença entre um modelo de dados completamente incorreto, em oposição a um que pode ser aprimorado à medida que obtemos maior entendimento do domínio, é o modelo que mapeia coerentemente o mundo real. Isso significa que devemos fazer todos os esforços para criar um modelo de dados inicial que seja consistente com nossa compreensão do problema em termos de suas entidades do mundo real. Se começarmos a normalizar as entidades erradas, o modelo de dados estará errado de duas maneiras e a recuperação será difícil.

De várias maneiras, a mudança para soluções de banco de dados "No SQL" é resultado dos problemas de incoerência do modelo de dados. Utilizar uma abordagem No SQL orientada a objetos nos leva a pensar mais sobre o mapeamento entre nossos objetos no código e os do mundo real - e quando encontramos uma inconsistência, isso geralmente é evidente porque é inviável implementar em nosso base de dados. Isso leva a um melhor design geral.

Isso leva à pergunta final: um modelo de dados relacionais é inconsistente com a abordagem ágil?

Não, mas é necessária mais habilidade. Enquanto no mundo sem SQL, é trivial adicionar um campo ou converter uma propriedade em uma matriz, não é nada trivial fazer essas coisas no mundo relacional. É preciso, no mínimo, alguém capaz de entender o modelo de dados relacionais e as entidades do mundo real que eles representam. Essa pessoa é a pessoa que facilitará a atualização do modelo relacional à medida que a compreensão do modelo do mundo real for alterada. Não existe uma bala de prata para resolver este problema.

theMayer
fonte
1
Eu realmente espero que você tenha superdimensionado o problema de criar um novo campo na tabela RDBMS para tornar a declaração mais dramática. A tabela do banco de dados precisa ser muito especial (ou o novo tipo de campo precisa ser algo excepcional) para realmente criar um problema para adicionar um campo.
Alexey Zimarev
Sim, mas nunca é apenas um campo ...
theMayer
1
Eu diria mais frequentemente que é apenas um campo. Mudanças dramáticas no esquema não são tão frequentes. Não sou fã de usar RDBMSes com design OO devido a incompatibilidade de impedância. No entanto, adicionar novos tipos (tabelas) e propriedades (colunas) é relativamente fácil nos dois mundos, embora no NoSQL seja realmente um pouco mais fácil. Mas mudanças complexas são dolorosas nos dois casos. Pior ainda, torna-se no sistema de origem de evento com snapshots, ao contrário de quão agradável é a experiência de desenvolvimento para esse sistema.
Alexey Zimarev
Vejo que os bancos de dados relacionais são frequentemente usados ​​como o "martelo universal" para resolver as necessidades de armazenamento de dados - quando, de fato, existem razões muito específicas para usá-los. Em um sistema cuidadosamente contemplado, raramente é necessário se preocupar com os problemas que escrevi na minha resposta - estou dirigindo-me a um público mais geral que talvez não tenha experiência para chegar a um projeto de sistema apropriado antecipadamente.
theMayer
Não há discrepância entre o modelo relacional e ele geralmente é mapeado para o mundo real, assim como qualquer outro tipo de modelo. Algumas operações serão mais fáceis com um tipo e outras com outro tipo. O problema é quando você cria um modelo de um tipo (orientado a objetos) e tenta implementá-lo com ferramentas de outro tipo (relacionais). Isso não funciona bem. Mas o mundo real não é orientado a objetos. É apenas e você o modela. E precisa usar as ferramentas certas para o tipo de modelo selecionado.
Jan Hudec
-1

O ponto principal não é refatorar tanto que seu modelo muda além de qualquer reconhecimento. Mesmo com o desenvolvimento iterativo, você deve realmente construir sobre o material existente e não refatorá-lo em pedaços.

Isso fornece duas opções principais para lidar com grandes mudanças quando elas surgirem: a primeira é criar a camada de banco de dados como uma API, usar procedimentos armazenados para que eles possam ser alterados para se adequarem ao cliente sem alterar o esquema de dados subjacente.

A outra maneira é substituir as tabelas por um pouco de migração de dados. Quando uma alteração em grande escala é necessária, você cria o novo esquema e implementa um conjunto de scripts para levar os dados antigos e massageá-los para o novo formato. Custa tempo para fazer isso, e é por isso que você confia mais em métodos mais baratos de modificar o acesso aos dados (por exemplo, via SPs) como primeira opção.

Então: 1. tente pensar no futuro com design para não precisar mudar as coisas.

  1. Confie em wrappers ou APIs para que as alterações sejam limitadas ou ocultas em um componente isolado

  2. Reserve um tempo para atualizar corretamente, se necessário.

Essas etapas se aplicam a tudo, não apenas aos bancos de dados.

gbjbaanb
fonte
O esquema subjacente às vezes precisa ser alterado. À medida que o aplicativo entra no teste do cliente, surgem novos atributos que você nunca ouviu falar, atributos que você achava que números eram cadeias de caracteres, relações que você esperava que fossem 1: 1 acabam não sendo assim, afinal de contas e assim por diante. Você não pode cobrir esse tipo de coisa por trás dos procedimentos armazenados (além disso, os procedimentos armazenados fazem parte do problema, porque, como outras coisas no banco de dados, eles não vivem no controle de versão).
Jan Hudec 22/02
@JanHudec desde quando os SPs não vivem no controle de versão? Você pode cobrir essas coisas, alterar a API do SP para pegar uma string e gravá-la em um campo diferente, manipulando os números antigos e as novas em um pouco de código no seu SP. Não é o mais legal, mas pode ser melhor do que ir a todos os sites de clientes para migrar seus dados para o novo formato de string (existem exemplos melhores, mas você entendeu). Se a mudança for grande, você precisará migrar, mas pelo menos com uma API de banco de dados, você também terá outras opções mais baratas.
Gbjbaanb
Você ainda precisa ir ao site de cada cliente para instalar o SP e adicionar o novo campo. E quando você estiver lá, também poderá migrar os dados. Os SPs são úteis, pois permitem criar uma interface compatível com versões anteriores, se você tiver vários aplicativos acessando o banco de dados, para que não precise atualizar todos eles ao mesmo tempo. Mas eles não salvam nenhuma etapa quando o esquema precisa mudar devido a alterações nos requisitos.
Jan Hudec