Bases de datos relacionales y desarrollo iterativo.

19

En muchos enfoques para el desarrollo de software, como las metodologías ágiles, el diseño basado en dominios y el análisis y diseño orientados a objetos, se nos alienta a adoptar un enfoque iterativo para el desarrollo.

Por lo tanto, se supone que no debemos hacer nuestro modelo de dominio correctamente la primera vez que comenzamos a trabajar en el proyecto. En cambio, a medida que pasa el tiempo, refactorizamos el modelo porque obtenemos una comprensión más profunda del dominio del problema con el tiempo.

Aparte de eso, incluso si tratamos de obtener un modelo perfecto por adelantado, que ya estoy convencido de que es muy difícil, los requisitos pueden cambiar. Entonces, después de que el software se haya implementado en producción, los usuarios finales pueden notar que cierto requisito no se entendió completamente o, lo que es peor, falta algún requisito.

El punto aquí es que podemos terminar necesitando cambiar el modelo después de que se haya implementado el software. Si esto sucede, tenemos un problema: la base de datos de producción tiene datos de usuario que son importantes y ya está ajustada en el formato del modelo anterior .

La actualización del código puede ser una tarea difícil si el código no está bien diseñado y si el sistema es grande. Pero se puede hacer con el tiempo, tenemos herramientas como Git que nos ayudan a hacerlo sin dañar la versión lista para producción.

Por otro lado, si el modelo cambia, si las propiedades de las clases desaparecen o lo que sea, la base de datos también debería cambiar. Pero tenemos un problema: ya hay datos allí que no se pueden perder, que ya están formateados para el modelo anterior.

Parece que una base de datos relacional aquí está siendo una barrera que nos impide realizar un desarrollo iterativo e incluso actualizar el software cuando lo requieren los usuarios finales.

Un enfoque que ya utilicé fue codificar una clase especial que asigna tablas de bases de datos antiguas a nuevas. Por lo tanto, estas clases recogen datos en formato antiguo, los convierten al formato utilizado por el nuevo modelo y los guardan en las nuevas tablas.

Este enfoque parece no ser el mejor. Mi pregunta aquí es: ¿existen enfoques conocidos y recomendados para conciliar el desarrollo iterativo con bases de datos relacionales?

usuario1620696
fuente
66
Por cierto, no creo que esto tenga nada que ver con las bases de datos relacionales en particular. Tengo un problema similar con un proyecto en el que estoy trabajando, pero lo estamos teniendo con el esquema de nuestras cadenas JSON que representan objetos muy no relacionales. Probablemente afecta a todas las formas de persistencia por igual.
Ixrec
1
Cambia el esquema de la base de datos de manera que no pierda datos, en.wikipedia.org/wiki/Schema_migration .
RemcoGerlich
1
Estoy seguro de que este tema se discutió extensamente en algún lugar antes, simplemente no puedo encontrarlo en Programadores. Pero vea aquí martinfowler.com/articles/evodb.html o aquí stackoverflow.com/questions/334059/…
Doc Brown
1
"Aparte de eso, incluso si tratamos de obtener un modelo perfecto por adelantado, que ya estoy convencido de que es muy difícil, los requisitos pueden cambiar". Me gustaría agregar que ni siquiera debería intentar obtener un modelo (casi perfecto) por adelantado. Eso podría vincular su mentalidad a un tipo de soluciones en lugar de mantener abiertas sus opciones.
Doblado

Respuestas:

15

No tiene que ser clases especiales, pero sí, necesita algo que tome la base de datos en formato anterior y la convierta en la actual.

La cuestión aquí es que necesita desarrollar un proceso para escribir y probar estos scripts y disciplina para nunca tocar las bases de datos de prueba y producción a mano, sino siempre mediante scripts de migración.

Cada vez que necesita hacer un cambio en la base de datos, escribe un script que lo hará, ya sea en SQL o utilizando su capa ORM, y lo confirma en el control de su versión junto con los cambios que requieren el nuevo esquema. Luego, tiene un script de control que actualizará la base de datos aplicando todos los scripts de migración que aún no se aplicaron en una secuencia.

Y asegúrese de que solo modifique los entornos de desarrollo, prueba y control de calidad compartidos aplicando los scripts y retrocediendo a la versión anterior si no funcionan, de modo que pueda estar razonablemente seguro de que funcionarán según lo previsto cuando los desate en la producción .

La nueva instalación se realiza simplemente aplicando todos los scripts. Después de un tiempo, es posible que tenga cientos de ellos y piense que es muy ineficiente, pero no caiga en la trampa de tratar de optimizarlo. La instalación es una actividad que se realiza una sola vez y mantenerla confiable es más rápida.

@Doc Brown ya vinculó a Martin Fowler: Diseño de base de datos evolutiva y /programming/334059/agile-development-and-database-changes , y agregaría Alex Papadimoulis: Cambios de base de datos hechos a la derecha , que es más corto y tiene algunos ejemplos.

Como un buen ejemplo de herramienta para implementar dicho proceso, sugiero Alembic . Se basa en el marco Python SQLAlchemy , pero puede usarlo con otros lenguajes y marcos si no tienen su propio soporte de migración. La página de Wikipedia sobre Schema Migration enumera más herramientas de este tipo .

Jan Hudec
fuente
1
@Tibo construyes el esquema desde cero ejecutando la misma secuencia de scripts. Así es como manejas el problema. Dado que, como estándar , puede acceder desde cualquier instancia de la base de datos, incluida una que aún no existe, a un esquema actual y tener la confianza de que es lo mismo. No es necesario tener dos formas según su ejemplo. (Al menos no se le da una línea de base consistente; el primer paso es establecer la línea de base y una vez que llegue a esa línea de base, el problema desaparece)
Murph
1
Aprobado por el artículo de Alex; Puede que no sea más corto, pero hace una lectura mucho más práctica y entretenida.
Murphy
1
Somos una tienda ágil y tenemos un servicio de tiempo de actividad del 100% y ambos se aplican también a la base de datos. Migramos el esquema de producción en promedio una vez al día y yo secundaría todo lo que Jan ha dicho. Una cosa adicional que hacemos que ha sido invaluable es lo que llamamos pruebas de migración, esto se ejecuta como parte de nuestro proceso de construcción e implementación. Toma una instantánea del esquema de la producción, aplica las migraciones pendientes del maestro a él y luego ejecuta las pruebas unitarias del código de producción implementado actualmente en ese esquema. El objetivo es verificar que la aplicación de las migraciones no rompa el sistema en ejecución.
Gordon Wrigley el
1

Por extraño que parezca, este es el verdadero problema que enfrenta mi equipo de desarrollo actual. La pregunta contiene varias subpreguntas, por lo que se abordarán de forma independiente.

En primer lugar, ¿una base de datos relacional restringe demasiado el modelo de datos y dificulta los cambios?

Ciertamente , pero no necesariamente por las razones citadas. Desafortunadamente, la versatilidad de los sistemas de gestión de bases de datos relacionales también conduce a su caída. El RDBMS se desarrolló originalmente para ofrecer una plataforma de almacenamiento de datos relativamente simple que aceptaría grandes conjuntos de datos y los reduciría a un tamaño relativamente pequeño. Esto se hizo a expensas de la complejidad en el modelo de datos y la potencia de cálculo requerida. A medida que aumentaba la complejidad de la base de datos, surgieron procedimientos almacenados, vistas, funciones y disparadores para ayudar a los administradores de bases de datos a lidiar con la complejidad de manera consistente y escalable.

Desafortunadamente, el modelo de base de datos relacional no está orientado a objetos y, naturalmente, no se asigna a entidades del mundo real como debería hacerlo un modelo de datos. Eso nos lleva a la necesidad de herramientas de intermediarios como mapeadores relacionales de objetos y similares. Desafortunadamente, si bien estas herramientas claramente tienen un lugar en el mundo del desarrollo actual, su uso se dirige simplemente a un síntoma del problema de complejidad de datos relacionales, en lugar de la causa subyacente, que es una desalineación del modelo de datos con el mundo real.

Eso lleva a la segunda parte de la pregunta, que en realidad era más una suposición, pero debería verse como una pregunta: ¿se supone que debemos hacer nuestro modelo de dominio bien la primera vez?

Sí, hasta cierto punto. Como señaló la pregunta, rara vez es posible comprender completamente el problema cuando comenzamos el proceso de diseño. Sin embargo, la diferencia entre un modelo de datos completamente incorrecto, a diferencia de uno que puede modificarse a medida que adquirimos una mayor comprensión del dominio, es el modelo que se asigna coherentemente al mundo real. Esto significa que debemos hacer todo lo posible para crear un modelo de datos inicial que sea consistente con nuestra comprensión del problema en términos de sus entidades del mundo real. Si comenzamos a normalizar las entidades incorrectas, el modelo de datos estará equivocado de dos maneras y la recuperación será difícil.

En muchos sentidos, el cambio a soluciones de base de datos "Sin SQL" es el resultado de los problemas de incoherencia del modelo de datos. La utilización de un enfoque orientado a objetos sin SQL nos hace pensar más sobre el mapeo entre nuestros objetos en código y aquellos en el mundo real, y cuando nos encontramos con una inconsistencia, a menudo es evidente porque no es factible implementarlo en nuestro base de datos. Esto conduce a un mejor diseño general.

Eso lleva a la pregunta final: ¿ es un modelo de datos relacionales inconsistente con el enfoque ágil?

No, pero se requiere más habilidad. Mientras que en el mundo sin SQL, es trivial agregar un campo o convertir una propiedad en una matriz, no es trivial hacer estas cosas en el mundo relacional. Se necesita, como mínimo, alguien que sea capaz de comprender tanto el modelo de datos relacionales como las entidades del mundo real que representan. Esta persona es la persona que facilitará la actualización del modelo relacional a medida que cambie la comprensión del modelo del mundo real. No hay una bala de plata para resolver este problema.

theMayer
fuente
1
Realmente espero que haya aumentado el problema de crear un nuevo campo en la tabla RDBMS para hacer que la declaración sea más dramática. La tabla de la base de datos debe ser muy especial (o el nuevo tipo de campo debe ser algo excepcional) para crear realmente un problema al agregar un campo.
Alexey Zimarev
Sí, pero nunca es solo un campo ...
theMayer
1
Yo diría que más a menudo es solo un campo. Los cambios dramáticos en el esquema no son tan frecuentes. No soy fanático de usar RDBMS con diseño OO debido a la falta de coincidencia de impedancia. Sin embargo, agregar nuevos tipos (tablas) y propiedades (columnas) es relativamente fácil en ambos mundos, aunque en NoSQL sí es un poco más fácil. Pero los cambios complejos son dolor en ambos casos. Peor aún se vuelve en el sistema de origen de eventos con instantáneas, en contraste con lo placentera que es la experiencia de desarrollo para dicho sistema.
Alexey Zimarev
Veo que las bases de datos relacionales a menudo se usan como el "martillo universal" para resolver las necesidades de almacenamiento de datos, cuando en realidad hay razones muy específicas para usarlas. En un sistema cuidadosamente contemplado, uno rara vez tiene que preocuparse por los problemas sobre los que escribí en mi respuesta: me dirijo a un público más general que puede no tener la experiencia para llegar a un diseño de sistema apropiado por adelantado.
theMayer
No existe discrepancia entre el modelo relacional y, por lo general, se asigna al mundo real tan bien como a cualquier otro tipo de modelo. Algunas operaciones serán más fáciles con un tipo y otras con otro tipo. El problema es cuando crea un modelo de un tipo (orientado a objetos) e intenta implementarlo con herramientas de otro tipo (relacional). Eso no funciona bien. Pero el mundo real no está orientado a objetos. Simplemente es y tú lo modelas. Y tiene que usar las herramientas adecuadas para el tipo de modelo seleccionado.
Jan Hudec
-1

El punto principal no es refactorizar tanto que su modelo cambie más allá de todo reconocimiento. Incluso con el desarrollo iterativo, realmente debería construir sobre las cosas existentes y no refactorizarlas en pedazos.

Esto le brinda 2 opciones principales para manejar grandes cambios cuando se presenten: la primera es construir la capa de base de datos como API, usar procedimientos almacenados para que se puedan cambiar para adaptarse al cliente sin cambiar el esquema de datos subyacente.

La otra forma es reemplazar las tablas con un poco de migración de datos. Cuando se requiere un cambio a gran escala, crea el nuevo esquema e implementa un conjunto de scripts para tomar los datos antiguos y aplicarlos en el nuevo formato. Cuesta tiempo hacer esto, por lo que confía más en métodos más baratos para modificar el acceso a los datos (por ejemplo, a través de SP) como primera opción.

Entonces: 1. trate de pensar con anticipación con el diseño para no tener que cambiar las cosas.

  1. Confíe en envoltorios o API para que el cambio sea limitado o pueda ocultarse dentro de un componente aislado

  2. Tómese el tiempo para actualizar correctamente si es necesario.

Estos pasos se aplican a todo, no solo a las bases de datos.

gbjbaanb
fuente
El esquema subyacente a veces necesita ser cambiado. A medida que la aplicación ingresa a la prueba del cliente, surgen nuevos atributos de los que nunca ha oído hablar, atributos que pensó que los números resultan ser cadenas, las relaciones que esperaba que fueran 1: 1 resultan no ser así, después de todo, etc. No puede cubrir este tipo de cosas detrás de los procedimientos almacenados (además, los procedimientos almacenados son parte del problema, porque, como otras cosas en la base de datos, no viven en el control de versiones).
Jan Hudec
@JanHudec desde cuándo los SP no viven en el control de versiones? Puede cubrir tales cosas, cambia la API de SP para tomar una cadena y escribirla en un campo diferente, manejando los números antiguos y las nuevas cadenas en un poco de código en su SP. No es el mejor, pero puede ser mejor que ir a cada sitio del cliente para migrar sus datos al nuevo formato de cadena (hay mejores ejemplos, pero se entiende). Si el cambio resulta ser grande, entonces tiene que migrar, pero al menos con una API DB también tiene otras opciones más baratas.
gbjbaanb
Todavía tiene que ir a cada sitio del cliente para instalar el SP y agregar el nuevo campo. Y cuando esté allí, también puede migrar los datos. Los SP son útiles porque le permiten crear una interfaz compatible con versiones anteriores si tiene múltiples aplicaciones que acceden a la base de datos, por lo que no tiene que actualizarlas todas al mismo tiempo. Pero no guardan ningún paso cuando el esquema necesita cambiar debido a los requisitos cambiantes.
Jan Hudec