关系数据库和迭代开发


19

在许多软件开发方法中,例如敏捷方法论,领域驱动设计和面向对象的分析与设计,都鼓励我们采用一种迭代方法进行开发。

因此,我们不应该在第一次开始从事该项目时就正确完成我们的领域模型。相反,随着时间的流逝,我们重构模型,因为随着时间的流逝,我们对问题领域有了更深入的了解。

除此之外,即使我们已经尝试过建立一个完美的模型(我已经确信这很困难),需求也可能会发生变化。软件打完已经被部署到生产,最终用户可能会注意到,有一定要求的不完全了解,或者更糟的是,一些要求失踪了。

这里的要点是,在软件部署之后,我们可能最终需要更改模型。如果发生这种情况,我们就会遇到问题:生产数据库中的用户数据很重要,并且已经以旧模型的格式进行了拟合

如果代码设计不当且系统很大,则更新代码可能是一项艰巨的任务。但这可以随着时间的推移而完成,我们拥有类似Git的工具,可以帮助我们做到这一点,而不会损坏可投入生产的版本。

另一方面,如果模型改变,类的属性消失或其他原因,则数据库也应改变。但是我们有一个问题:已经有不能丢失的数据,已经为旧模型格式化了。

关系数据库似乎是阻碍我们进行迭代开发甚至在最终用户需要时更新软件的障碍。

我已经使用的一种方法是编写一个特殊的类,该类将旧的数据库表映射到新的数据库表。因此,这些类选择旧格式的数据,将其转换为新模型使用的格式,然后保存到新表中。

这种方法似乎不是最好的方法。我在这里的问题是:是否存在任何众所周知的和推荐的方法来协调迭代开发与关系数据库?


6
顺便说一句,我认为这与关系数据库没有任何关系。我正在处理的项目也有类似的问题,但我们将其与代表非常非关系对象的JSON字符串的架构一起使用。它可能平等地影响所有形式的持久性。
Ixrec

1
您可以使用不会丢失数据的方式更改数据库架构,请访问en.wikipedia.org/wiki/Schema_migration
RemcoGerlich '16

1
我敢肯定,以前在某个地方已经广泛讨论了这个主题,但是在Programmers上找不到它。但请参阅martinfowler.com/articles/evodb.htmlstackoverflow.com/questions/334059/…–
布朗

1
“除此之外,即使我们试图预先建立一个完美的模型(我已经确信这是非常困难的),需求也可能会改变。” 我想补充一点,您甚至不应该尝试预先建立(接近完美)模型。这可能会将您的思维方式限制为一种解决方案,而不是让您的选择保持开放状态。
弯曲

Answers:


15

它不必是特殊的类,但是是的,您需要将数据库以以前的格式转换为当前格式的内容。

此处的问题是,您需要开发一种用于编写和测试这些脚本的流程,并且要遵守纪律,以免手动操作测试和生产数据库,而始终使用迁移脚本。

每次需要对数据库进行更改时,无论是在SQL中还是使用ORM层,都要编写一个脚本来执行此操作,并将其与需要新架构的更改一起提交给版本控制。然后,您将拥有一些控制脚本,这些脚本将通过依次应用所有尚未应用的迁移脚本来升级数据库。

并确保仅通过应用脚本修改所有共享的开发,测试和质量检查环境,如果它们不起作用,则回滚到早期版本,因此,当您将它们释放到生产环境中时,您可以合理地确信它们将按预期工作。

只需应用所有脚本即可完成新安装。一段时间之后,您可能会拥有数百个,并认为它效率很低,但是不要陷入尝试对其进行优化的陷阱。安装是一项一次性的活动,请使其可靠地胜过一切,以使其快速进行。

@Doc Brown已经链接了Martin Fowler:Evolutionary Database Design/programming/334059/agile-development-and-database-changes,并且我要添加Alex Papadimoulis:Database Changes Done Right,它较短并有一些例子。

作为实现此类过程的工具的一个不错的例子,我建议使用Alembic。它基于Python SQLAlchemy框架,但是如果其他语言和框架没有自己的迁移支持,则可以将其与其他语言和框架一起使用。Schema Migration上的Wikipedia页面列出了更多此类工具


1
@Tibo通过运行相同的脚本序列从头开始构建模式。这就是您解决问题的方式。有了这种标准,您就可以从数据库的任何实例(包括尚不存在的实例)到当前模式,并确信它们是相同的。根据您的示例,无需使用两种方法。(至少没有给出一致的基线-第一步是建立基线,一旦达到该基线,问题就消失了。)
Murph

1
赞成Alex的文章;它可能不会更短,但是会使阅读更加注重实践性和娱乐性。
墨菲

1
我们是一家敏捷商店,我们提供100%的正常运行时间服务,这两种情况同样适用于数据库。我们平均每天迁移一次生产模式,我将把Jan所说的一切都放在第二位。我们所做的另一无价的事情就是所谓的迁移测试,它是我们构建和部署过程的一部分。它从生产环境中删除了一个架构快照,将任何从主服务器的暂挂迁移应用到它,然后针对该架构从当前部署的生产代码运行单元测试。目的是检查应用迁移不会破坏正在运行的系统。
Gordon Wrigley's

1

奇怪的是,这是我目前的开发团队面临的问题。该问题包含几个子问题,因此将对其进行独立处理。

首先,关系数据库是否会过度约束数据模型,从而使更改非常困难?

最肯定的,但不一定是引用的原因。不幸的是,关系数据库管理系统的多功能性也导致了它们的崩溃。RDBMS最初是为了提供一个相对简单的数据存储平台而开发的,该平台可以接受大数据集并将其缩减为相对较小的大小。这样做是以牺牲数据模型的复杂性和所需的计算能力为代价的。随着数据库复杂性的增加,存储过程,视图,功能和触发器应运而生,以帮助数据库管理员以一致且可扩展的方式处理复杂性。

不幸的是,关系数据库模型不是面向对象的,并且自然不会像数据模型那样映射到真实世界的实体。这导致我们需要像对象关系映射器之类的中间人工具。不幸的是,尽管这些工具在当今的开发世界中显然占有一席之地,但它们的使用仅针对关系数据复杂性问题的症状,而不是根本原因,后者是数据模型与现实世界的错位。

这就引出了问题的第二部分,它实际上更多地是一个假设,但应视为一个问题:我们是否应该在第一时间就完成我们的领域模型?

是的,在一定程度上。正如问题所指出的那样,当我们开始设计过程时,很难完全理解问题。但是,完全不正确的数据模型与可以随着我们对领域的深入了解而进行调整的数据模型之间的区别在于,该模型一致地映射到了现实世界。 这意味着我们必须尽一切努力来创建一个初始数据模型,该模型与我们对问题的真实实体的理解相一致。 如果我们开始对错误的实体进行规范化,则数据模型将以两种方式出现错误,并且恢复将很困难。

在许多方面,迁移到“ No SQL”数据库解决方案是数据模型不一致问题的结果。使用面向对象的No SQL方法会使我们更多地考虑代码中的对象与现实世界中的对象之间的映射,当我们遇到不一致时,这通常是不言而喻的,因为无法在我们的代码中实现数据库。这导致更好的总体设计。

这就引出了最后一个问题:关系数据模型是否与敏捷方法不一致?

不,但是需要更多技能。 在No-SQL世界中,添加字段或将属性转换为数组是微不足道的,而在关系世界中执行这些操作则并非微不足道。它至少需要一个能够理解关系数据模型及其所代表的现实世界实体的人。这个人是随着对真实世界模型的理解的变化而将有助于更新关系模型的个人。没有解决这个问题的灵丹妙药。


1
我真的希望您过大了在RDBMS表中创建新字段的问题,以使该语句更加生动。数据库表必须非常特殊(或者新字段类型需要特殊)才能真正创建添加一个字段的问题。
Alexey Zimarev '16

是的,但它绝不仅仅是一个领域……
Mayer

1
我会说更多的只是一个领域。戏剧性的模式更改并不常见。由于阻抗不匹配,我不喜欢将RDBMSes与OO设计一起使用。但是,尽管在NoSQL中确实要容易一些,但是在两个领域中添加新的类型(表)和属性(列)都相对容易。但是复杂的变化在两种情况下都是痛苦。甚至更糟的是,在具有快照的事件源系统中,这种系统的开发体验变得多么令人愉悦。
Alexey Zimarev '16

我看到关系数据库通常被用作解决数据存储需求的“万能锤子”-实际上,有非常特定的原因来使用它们。在经过仔细考虑的系统中,很少有人会担心我在回答中写的问题-我是在针对可能没有经验来预先进行适当的系统设计的更广泛的受众群体。
theMayer '16

关系模型之间没有差异,它通常与任何其他类型的模型一样映射到现实世界。一种操作会更容易,而另一种操作会更容易。问题是当您创建一种模型(面向对象)并尝试使用另一种工具(关系型)来实现它时。那不是很好。但是现实世界不是面向对象的。就是这样,您可以对其建模。并且必须为所选的模型使用正确的工具。
Jan Hudec

-1

要点是不要重构得太多,以至于您的模型发生了无法识别的变化。即使进行了迭代开发,您实际上也应该在现有内容的基础上构建而不是将其重构。

这为您提供了两个主要的选择来处理大的变化:第一个是将DB层构建为API,使用存储过程,以便可以更改它们以适合客户端,而无需更改基础数据架构。

另一种方法是用少量数据迁移替换表。当需要进行大规模更改时,可以创建新的架构并实现一组脚本以获取旧数据并将其整理为新格式。这样做会花费时间,这就是为什么您首先选择更便宜的方法来修改对数据的访问(例如,通过SP)的原因。

因此:1.尝试先思考设计,以便您无需进行任何更改。

  1. 依靠包装器或API,因此更改受到限制或可以隐藏在隔离的组件内部

  2. 如果需要,请花一些时间正确升级。

这些步骤适用于所有内容,而不仅仅是数据库。


有时需要更改基础方案。当应用程序进入客户测试时,新的属性突然出现,您从未听说过,您认为数字的属性原来是字符串,预期为1:1的关系最终并非如此,依此类推。您无法在存储过程后面介绍这类问题(此外,存储过程是问题的一部分,因为像数据库中的其他事物一样,它们不存在于版本控制中)。
Jan Hudec

@JanHudec,因为什么时候SP不在版本控制中?您可以涵盖这些内容,您可以更改SP API以采用字符串并将其写入其他字段,并在SP中的一些代码中处理旧数字和新字符串。这不是最好的方法,但是比到每个客户站点将其数据迁移到新的字符串格式更好(有更好的示例,但您可以理解)。如果变化很大,那么您就必须迁移,但是至少对于DB API而言,您还有其他更便宜的选择。
gbjbaanb

您仍然必须转到每个客户站点以安装SP并添加新字段。而且当您在那里时,您也可以迁移数据。SP很有用,因为如果您有多个应用程序访问数据库,它们就可以创建向后兼容的接口,因此您不必同时升级所有它们。但是当架构由于需求变化而需要更改时,它们不保存任何步骤。
Jan Hudec
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.