【问题标题】:Is it possible to use feature flags/toggles when feature modify database?功能修改数据库时是否可以使用功能标志/切换?
【发布时间】:2020-04-03 01:58:36
【问题描述】:

在考虑在应用程序中使用功能切换时,这个问题最让我困惑。大多数功能都需要对数据库进行一些更改。那么如何实现功能标志以在打开/关闭功能切换的同时顺利恢复数据库更改?特别是当使用某些数据库迁移工具时,例如 flyway 或 liquibase。

让我们举个例子。例如,我有带有 Id、Name 列的简单表播放列表。我想在应用程序中添加向播放列表添加描述的功能。数据库表需要更改。已添加新列。列值不是强制性的。到目前为止很简单。一段时间后,功能将从生产中关闭。解决方案非常简单但很混乱——我们可以将列保留在 db 中,不再在代码中使用它。迁移工具以增量方式工作,因此当我关闭功能切换(或者我错了吗?)时,我看不到任何返回到以前的数据库状态的选项。更难的例子是当字段需要是强制性的。然后我不能只将该字段留在数据库中。它将向后不兼容。那是什么?如何处理这种情况?我认为这是很常见的情况。

此外,即使对 db 稍作更改,我的该类的应用程序模型也会发生变化。 (假设使用了一些 ORM)。这不像用策略模式替换实现那么容易。除非您为 ORM 模型的使用提供大量抽象。所以添加这样一个特征标志似乎很复杂。谁能帮我理解一下如何使用功能工具?或者也许有人有完整的例子来展示?首选Java

【问题讨论】:

    标签: java liquibase flyway featuretoggle


    【解决方案1】:

    功能切换是一种在不更改代码的情况下更改行为的方式 - 这意味着它们也与数据库架构更改无关。关闭切换应该做一些可逆的事情,比如从 UI 中隐藏一个字段,而不是像删除数据库列这样永久的事情。考虑到这一点,您的示例将如下所示:

    1. 播放列表发布 v1
    2. 在 v2 中,为描述添加 UI,但被功能标志禁用。迁移数据库以添加新的描述列。发货。
    3. 启用功能标志。用户现在可以使用描述
    4. 禁用功能标志。用户不能使用描述,但它仍在应用程序和数据库中
    5. 在 v3 中,移除休眠的 UI 元素和逻辑。迁移数据库以删除不需要的描述列。发货。

    【讨论】:

    • 感谢您的回答。这是有道理的,但是当我添加必填字段时,我仍然不知道该怎么做。然后它是向后不兼容的,我不能在步骤 2 中使用它。同样在我的数据库映射类中我需要添加它,然后上层方法需要有一个选项来创建带有描述的播放列表。在 MVC 控制器中也受到影响,因为我需要为此设置不同的端点(也许一些版本控制在这里会很好)。正如我所见,有很多变化很难关闭或打开。
    • 如果描述是强制性的,也许你可以在你的数据库中添加一个默认值(空字符串等)
    • 另一种选择是在应用层而不是数据库层中强制执行字段(“描述”)的“强制”属性,从而能够通过该功能打开/关闭强制执行切换。
    【解决方案2】:

    同时支持同一系统的多个版本的问题是这个问题的根源,也是我正在积极思考的一个问题。让我们一次一件!

    什么是功能切换?

    对不起其他答案!功能切换只需要做一件事:将功能发布与部署分离。正如这个问题所指出的,切换如何针对任意更改执行此操作是一个棘手的问题。

    通常,为切换器供电的系统必须满足两个条件才能被认为是完整的:

    • 向后兼容性。无论此切换的状态如何,旧功能都必须仍然有效。这包括此功能的旧行为!
    • 向前兼容性。无论此切换的状态如何,新功能都必须正常运行。

    对于任何重要的更改,这都是...具有挑战性的。反正就是一个字?

    示例 1:用户界面样式

    假设您有一个无法访问其输入样式的表单。您的工作是修复样式以使其易于访问,但您使用 trunk-based development,并且您的团队希望在每天结束前将所有工作与您项目的唯一分支 master 集成。

    这种变化是极其孤立的。它影响:

    • 单一表格
    • 无功能(即无需更改业务逻辑)
    • 没有非功能性要求(例如,这不会引入扩展问题)

    因此,根据切换的状态切换加载哪个 CSS 样式表就足够了:

    • 向后兼容性:没有功能受到影响,所以旧的东西应该默认工作。关闭标志不需要特殊行为,因此只需要简单的条件逻辑即可。
    • 前向兼容性:这比较棘手,但假设表单是正确的DRYed out,任何新输入都将自动继承标志指示的样式。假设 separation of concerns 良好,对这些样式的更改不会影响任何其他组件,反之亦然。

    示例 2:一个新的表单域

    我希望你喜欢这种美丽的简单,因为我们现在遇到了麻烦。这正是 OP 所描述的那种情况。

    此更改跨越多个系统。它影响(至少):

    • 用户体验表单
    • 后端的数据 API,因为有一个新字段
    • 数据库层,因为有一个新字段

    如此小的差异使事情变得更具挑战性。我们将在这里逐个系统地进行。

    用户体验形式:

    • 向后兼容性:与前面的示例相同。如果这确实是一个新领域,那么旧代码应该不在乎。此功能切换必须涵盖任何代码路径。
    • 前向兼容性:这里的主要问题是一个字段可能在某一天存在,然后在下一次切换时消失。新逻辑可能需要在前端状态管理中设置默认值,或者由后端提供。

    数据 API:

    • 向后兼容性:此字段表示 API 合同的更改。为了支持某些用例(想到验证),如果切换关闭,可能需要提供默认值。否则,尽管YMMV,旧的东西应该没问题
    • 前向兼容性:再一次,棘手的部分归结为确保在此切换关闭时有一些东西可供新代码使用。在最坏的情况下,可能需要将特殊的条件逻辑编码到新功能中以处理标志关闭的情况

    数据库层:

    • 向后兼容性:在数据库级别,向后兼容性要求我们只添加可选字段。必要性可以在我们的应用程序的其他地方强制执行,但是如果模式添加了新的必填字段,则不能将其视为向后兼容。旧的插入和更新将立即中断。因此,您的数据迁移添加了一个新的可选字段。容易吗?
    • 前向兼容性: 好的,新代码进来了。它应该期待这个字段吗?如果必须,这就是默认值发挥作用的地方。注意我没有指定什么应该声明默认值,因为这取决于应用程序,但必须有一些东西。在最坏的情况下,特殊的条件逻辑将不得不涵盖该字段是默认字段的可能性。

    这听起来很疯狂!如何使其易于管理?

    这里要遵循三个主要原则来保持理智:

    1. 让您的更改尽可能小,并重构阻止小更改的痛点。这意味着更多的标志,但更少的复杂性。渐进式改进是游戏的名称。
    2. 将长期存在的标志视为关键的技术债务。您的标志不应该在生产中持续很长时间。对更改何时“稳定”有明确的规则,以及清理相关标志的清晰窗口。作为维护的常规部分清理标志对于控制需要支持多少代码路径至关重要。
    3. 不要教条!抛弃极端,必要时使用长期存在的特性分支。有些更改已经太复杂了,标记的额外开销是不值得的。如果您遵循第 1 点和第 2 点,这种情况应该会越来越少。

    祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-12
      • 1970-01-01
      • 2019-01-10
      • 2021-03-22
      • 1970-01-01
      • 2019-12-01
      • 1970-01-01
      相关资源
      最近更新 更多