不使用DDD的后果:为什么我们停止了向微服务的迁移? - Steven Lemon

19-08-11 banq
                   

最近,我们的开发团队在功能交付计划方面略有突破。技术领导层决定,这次将我们的单片单体架构分解为微服务是最好的时机。经过一个月的调查和准备,我们却取消了这项迁移,而是决定坚持使用我们的单体巨石系统。

对我们来说,微服务不仅不会帮助我们; 还会伤害我们的开发进程。微服务作为理想的架构售给我们可能已经一年了。所以我们很惊讶地发现它并不适合我们。我认为提交一份关于我们经验的案例研究以及为什么我们的团队决定反对他们肯定会很有意思。

识别问题和早期妥协

我们非常依赖第三方,我们的应用程序是现有外部产品顶部的自定义UI,集成了我们的一些自定义业务规则并提供了一个触摸友好的用户界面。我们的客户是一个UWP应用程序,我们有一系列后端服务,可以在我们的域和第三方域之间进行转换。

建立在第三方之上的方式影响了我们如何将我们的域划分为微服务。例如,我们的应用程序偶尔必须在域之间转换功能。使第三方域的一部分行为并感觉它是我们UI中不同域的一部分。

当我们在前端和第三方之间提供单体一个大的服务时,这种转换并不是那么糟糕。但是,当我们尝试将域拆分为单独的微服务时,领域切换会给我们带来很大的困惑。

我们的微服务是否遵循与第三方相同的视角切分方式?我们如何在两个服务中复制前端的一个功能?我们是否根据我们的域划分了微服务,并且需要从第三方的两个独立区域获取一个微服务。

我们经常与外部团队合作,一个功能要求双方进行更改。第三方是另一个团队。密切合作意味着我们必须与他们的发布过程保持同步。微服务的一个好处是每个团队都可以负责独立发布他们的服务,而不需要与其他团队协调。不仅跨团队协调发布,而且跨公司协调发布使我们无法获得这些优势。

微服务的核心思想之一是拆分只负责某个单独层的独立团队。在微服务架构中,每个团队负责解决其业务问题的完整栈。对我们来说,由于我们的一个层是一个完全独立的公司,这种重组是不可能的。

我们无法充分隔离每个微服务

我们无法在我们的单体中发现任何明显的候选服务可被分解为微服务。因此,我们开始在我们的域模型之间绘制任意行,从中我们得到了我们要创建的微服务列表。但是,一旦我们开始调查,我们发现很多共享的业务逻辑很快就会在分离的微服务域之间的隐式耦合。进一步尝试将这些微服务细分为越来越小的部分,但这使得我们在各处都有更多的耦合,引入了消息总线,以及从一个服务到十个或更多微服务的潜在大爆炸。

一切都是如此耦合而且难以分解的原因是我们试图分离的单体只能满足一个业务问题。我们的客户端应用程序的总体设计目标之一是将第三方基础应用程序中的不同概念结合在一起。我们正在创建跨越域和分组功能的工作流,以方便用户。从本质上讲,用户接口过去四年一直在推动所有事情。

在某个地方,我们误解了微服务应该如何被隔离,并低估了选择服务之间正确边界的重要性。我们打破单体的唯一方法意味着实现标准“功能”将涉及同时更新多个微服务。每个功能都需要不同的微服务组合,这阻止了任何微服务都被一个团队拥有。

分享微服务

我们有大约12名开发人员分布在2个功能团队和一个支持团队中。工作波动很大,没有团队被锁定到应用程序的任何区域。让两个团队同时触及代码的相同区域并不罕见。我们无法将任何潜在微服务的所有权分配给单个团队。

在考虑建筑风格时,考虑Conway康威定律很有用。它声明您的软件架构是以模仿您的组织和团队结构的方式发展。如果你有一堆独立的团队处理不同的业务问题,那么很多独立的微服务都是有意义的。否则,几个团队共享功能的方式更合适。

该平台尚未准备好

各种问题意味着至少在6个月内,我们将在IIS中的整体块旁边托管我们的新微服务。我们无法访问许多与微服务相关的标准工具,如容器,Kubernetes,服务总线,API网关等。没有这些工具会使微服务更难以相互通信。因此,我们决定每个微服务都会复制任何共享逻辑以及来自存储层的常见读取和转换。因为我们无法正确隔离任何服务,这意味着我们将留下大量重复。例如,我们确定了一个特别复杂且必不可少的业务逻辑,必须在4个计划的微服务中进行复制粘贴和维护。

我们对未来没有清晰的了解

开发团队对接下来的6个月有一个粗略的想法,并没有关于超出这个范围的信息。此外,业务经常改变它的想法。更改中间功能的要求并不少见。这种不确定性使得创建微服务变得更加困难,因为我们无法预测即使在短期内会出现什么新的链接。计划的微服务之间的连接和耦合会增长吗?我们是否需要花几个月的时间再次将他们重新加入?我们已经尝试在今年早些时候创建一个证明概念微服务,但只是因为业务改变了它的要求而使它无法解决。

时间紧迫

我们只有一个小的时间窗口,在这段时间内匆忙地将大的整体分成我们需要的微服务列表。我们没有任何额外的时间让我们反思我们创造的东西或者如果需要改变路线。没有计划B的。我们将被困在我们创造的任何东西上。由于我们在规划阶段发现了许多问题和挑战,更不用说实施阶段了,这引起了开发团队的极大关注。

缺乏经验

使风险和时间压力更加复杂,负责架构或实施微服务架构的人员都没有任何特定的先前经验。由于没有很多标准工具可供使用而加剧了这一点,这意味着我们将自己实施该平台。与一些有微服务经验但没有参与的人交谈,引发了更多的危险信号。我们无法界定在基础设施或在域模型之间分界的后果。

到目前为止,我们的计划涉及许多妥协,这些妥协偏离了标准的微服务模式,时间紧迫。没有专家指导,很有可能犯下许多错误,并且很难学习。开发团队看起来很紧张。

我们想要再次实现什么目标?

一旦一切都开始变得艰难,前进的道路开始迷失,我们停下来,意识到我们不知道为什么我们这样做了。我们没有列出我们的痛点,我们也没有清楚地了解这将有助于解决我们所遇到的任何痛点。更糟糕的是,微服务可能正在为我们创造一整套新问题。

我们开始强调这些问题,我们应该获得哪些好处,以及我们要解决的问题是什么?我们设置越来越多的会议试图弄清楚,每个咖啡休息时间和开发人员之间的每次谈话都在讨论和质疑微服务,我们仍然无法直接回答原因。

事实证明,我们确实有其他更紧迫的痛点,在向微服务的驱动迁移中被忽略了。不幸的是,我们可能已经没有时间来充分解决这些问题,这意味着我们既没有微服务也没有其他任何东西。

有什么潜在的好处?

一旦我们意识到我们不知道为什么我们要走向微服务,我们暂停并开始自己调查微服务通常提供的好处。

自治​​​​​​​

微服务允许您的团队控制提供功能所需的完整堆栈。这种分离的好处是减少了与其他团队的协调量。你不会影响他们的工作,他们不会影响你的工作。

允许您的团队专业化

在单体巨石中,任何团队都可以最终处理任何事情。任何特征或区域的所有权都不是指定的。每个团队拥有自己的服务,他们可以在特定的业务关注点上建立专业知识。他们了解其领域中的业务规则和要求。他们知道他们的软件栈是如何构建和实现的,并且在进行更改时可以更有信心。

更容易扩展

使用微服务,您可以根据其性能需求扩展每个服务。使用整体,虽然您也可以在更多服务器上水平扩展,但您无法将整体的每个组件彼此分开。此外,微服务这种细粒度使得根据需要更容易地上下调整服务。也许你预计会有一些额外的负载,或者在解决性能问题时需要一些喘息空间。

更容易回滚

如果每个功能仅需要更改单个微服务,则可以回滚该功能而不会影响其他团队的工作。此外,微服务有助于减少单个故障可能导致的系统数量。

更容易发布,更容易更频繁地发布

如果您拥有广泛的系统,则每个版本都会变得耗时且有风险。回归测试需要涵盖很多内容,限制了您的发布节奏。您可能需要从多个人签名并在每个版本中涉及的所有团队之间进行协调。您从未听说过的团队中的错误或回归可以保留您需要的时间敏感功能。微服务限制了变更范围,减少了团队之间所需的协调量。团队可以按照他们自己的时间表发布,而不是受到单体巨石的节奏约束。

使用最合适的技术

微服务使您的团队能够为他们的团队选择最合适的技术以及他们试图解决的问题。也许他们可以使用现代技术,而单体巨石可能难以升级并停留在过时的平台上。

更容易升级

在最好的情况下,升级大型应用程序使用的框架永远不会有趣或没有风险。当您需要协调跨多个团队的广泛,相互关联的变更时,要困难得多。较小的隔离服务使您可以选择仅升级需要更新的服务,或者允许您一次执行一个服务和一个团队的升级。

防止变化

应用程序的不同部分以不同的速率变化。您的大多数应用程序可能在几个月甚至几年内没有被更改。将很少更改的代码与经常变动的区域分开,可以降低意外回归的风险。

较小​​​​​​​

较小的服务更容易推理和理解。此外,仅由一个团队进行更改意味着其设计保持一致。它更小的尺寸使得更容易进行广泛的重构。相比之下,单体巨石可能具有不一致的进化架构,因为不同团队的观点会导致其随时间变化。

结论的好处

采用微服务有很多潜在的好处。但是,我们能够获得任何一个吗?

最终,我们无法改变的部分架构和我们必须做出的妥协破坏了这些好处。微服务是一个在所有团队之间共享的浮动池,并且功能在多个共享微服务之间分散,这意味着我们失去了隔离的好处:减少了协调,专业化和从中获得的好处。微服务之间的差异,变得不利而不是一种力量了。每个功能都需要了解新的微服务如何工作以及其他团队对其做出的改变。我们对第三方的依赖阻止了我们改善依赖程度并降低了我们从独立扩展服务中获得的收益。

权衡利弊

用大象枪杀死一只苍蝇

采用微服务并不是免费的。您需要解决许多其他问题。我们需要重新审视我们之前在我们的单体巨石中提出的许多问题。例如,我们需要解决或重新审视:日志记录,监控,异常处理,容错,回退,微服务到微服务通信,消息格式,容器化,服务发现,备份,遥测,警报,跟踪,构建管道,发布管道,工具,共享基础架构代码,文档,扩展,时区支持,分阶段部署,API版本控制,网络延迟,运行状况检查,负载平衡,CDC测试,容错,调试以及在我们的本地开发环境中开发多个微服务。

更糟糕的是,如果没有准备好微服务平台,我们就必须为自己完成上述许多工作。我们已经遇到了痛点并且难以转向微服务; 我们确定我们不会受益于迁移到微服务的优势,我们有一长串额外的工作来建立和维护以支持微服务。

微服务仅限名称

下图展示了我们当前的整体结构,我们的计划架构以及微服务的外观比较。在结构上,我们的新架构仍然非常类似于我们的巨石,所有的东西仍然紧密地联系在一起。我们是否应该使用微服务标签来描述我们在做什么?

(banq注:中间的图新架构其实是传统SOA,服务中耦合了业务逻辑,从职责角度看,服务只是协作者,不是业务决定者,DDD领域模型才是决定者,饭店的服务员能做决定吗?当然当你退菜时,她会马上说菜已经在烧,不能退了,根本不会去业务决定者厨房那里看看,在这个案例中,第三方3id Party是业务决定者,如果业务决定者没有进行领域或有界上下文切分,也就是大厨没有分工,你责难服务员有意思吗?)

我们的巨石那么糟糕吗?

我们使用monolith(单体/巨石/整体)就像一个加载的术语。好像说“巨石”意味着一些可怕的东西,“微服务”意味着一些好东西。一旦我们看过刻板印象和品牌,开发团队对我们的“巨石”几乎没有什么问题。它可能是我们整个系统中最无痛的部分之一。开发和扩展是直截了当的,因为它主要是对第三方的直通。我们不需要花太多时间来研究它。我们有一个出色的CI / CD设置,这使得它易于部署和回滚。我们的分支和测试策略确保了很少将其投入生产的问题。

意识到我以前曾经使用过微服务

在这一点上,我意识到我确实有过以前角色的微服务经验。我们从来没有把它称为微服务,它可能没有遵循微服务的所有“规则”,但它确实解决了同样的问题并给了我们同样的好处。

我们是一个由约200名开发人员组成的团队中的一个小团队。也许5%的后端工作都在公司共享的巨型组件中,这是一个庞大的C#应用​​程序。剩下的时间,我们在两个Node服务中工作。

我们不喜欢在巨石中工作。它的工作速度很慢,编译和运行测试,架构变化到不可知,随机的东西不断出现在构建步骤中。多次我们为客户提供了一项高优先级的工作,因为一个我从未听说过的团队在功能上退步了。由于需要在整个公司内进行协调,因此定期技术更新需要数月时间。在我们等待完全独立的团队的批准时,拉动请求可能会持续数周。

同时,我们的两项服务都很小; 我们完全控制了他们的开发,架构和部署。一旦我们遇到性能问题,我们就会将生产中的实例数量增加一倍,直到我们解决了潜在的问题。我们很少与其他团队协调。在TypeScript中提供我们的服务使我们的主要前端开发人员团队在前端和后端使用相同的语言。最重要的是,它允许我们在我们的客户端和后端验证和报告服务中包含我们复杂的规则计算引擎。我们的团队专注于一个非常狭隘的业务问题,我们都成为了专家。

不仅仅是技术问题

我们对微服务的研究越多,对技术的关注就越少,对结构化团队和进入它们的工作的关注就越多。我们错误地将微服务视为纯粹的技术问题吗?

对于没有答案的大局,有很多问题。

*重组团队是否致力于将实际问题分开?

*我们可以干净地划分我们的域和微服务之间即将推出的功能吗?

*是否会为所有团队提供足够的工作,或者团队是否会无法工作?

*团队是否会受到大量高优先级工作的攻击,而这些工作是他们无法分享的?

*同样的问题是否会让我们难以分割我们的巨石还会阻止我们的管理层分割新工作?*他们对这种转变的兴趣是什么?

从a到b

我们计划进入微服务是一个巨大的爆炸。每个人都停止了几个月的功能工作,并开始拆分我们的巨石。尽管许多先决条件还没有准备好。我们正在迫使前进,而不是等待出现的需要或自然候选人出现。

这不仅是从a到b的非常好的方式,但它也是倒退的。首先创建所有微服务,然后为它们设置基础架构,并完全忽略构建团队和传入工作的方面。相反,如果我们开始通过围绕专门的业务问题重组我们的团队,然后准备好基础设施,我们就可以自然地出现微服务的舞台。如果出现任何新的业务问题,可以将它们直接放入新服务中。

通过强制微服务,这意味着我们还必须预先选择每个微服务的大小。关于每个微服务的大小(或小),存在许多相互矛盾的建议。一些文章建议每个微服务对于一个团队应该足够大。其他人建议每个微服务应该足够小,你可以将结构保持在头脑中,甚至可以在两周内重写它。其他建议他们应该是每个业务关注的大小。领导决定根据我们的域模型拆分我们的微服务,然后在出现任何问题时将它们分开。这导致了上面提到的许多问题,团队和功能需要共享微服务。事后看来,如果我们让其他所有东西到位后微服务自然出现,

取消

我们的团队不断发现越来越多的问题。创造更多妥协并进一步降低收益。从开始实施我们的微服务的第一个sprint开始四天,我们仍然无法确定任何收益,并且问题和缺点的列表足够长,足以形成这篇相当长的博客文章的种子。我们召开了一次会议,尽管领导层需要,但微服务的答案却写在每个开发人员的脸上。我们对微服务的迁移被取消了。

那我们做了什么呢?

转向微服务的热情意味着尚未对替代方案进行调查。只有在我们放弃微服务之后,我们才能调查其他选择。最终,我们开始将解决方案分解为现有巨石中的单独项目,而不是将我们的巨型单元分离为单独的服务。这种划分为我们提供了一些额外的结构,更好地指示了存在耦合和重复的位置,而没有微服务的额外重量和挑战。

此外,这种结构将使我们的领域模型更加清晰,使我们能够更容易地评估任何未来微服务的候选者。如果某些事情确实是一个合适的候选人,那么该项目就可以从我们的整体中“脱离”到一个微服务中,而不必被解开。

结论

领导力确定了微服务的方向,而没有考虑应用的挑战和状态。在评估之后,我们发现微服务不适合我们,并且需要重大妥协。妥协使我们失去了任何好处,并意味着转向微服务是一种净损失。已经决定使用微服务而不评估团队结构和传入工作等非技术问题。经过数月的调查和工作,我们放弃了这个项目,并花了剩余的时间对我们的“巨石”进行一些小的重构。

banq评:没有从业务下手,而只是从技术架构入手,方向错误,条条大路通罗马,但是去罗马还是去希腊的方向选择才是更重要的。

                   

3
401825317
2019-09-02 17:26

1