本文讨论软件设计中的决策,特别是关于将较大的系统拆分为多个可独立部署的服务端点。不会特别讨论【服务端点设计】,但我想探讨一下为创建多个服务应用程序进行构思的阶段。
面对复杂问题,通常试图理解复杂性的各部分。将问题拆解为更易于理解和处理的小模块,可以更有效地应对。
如同在许多产品/项目管理周期中描述的,对现实生活问题,通常直觉驱动。我们并没有使用某种公式理解前往需要签证的国家所需步骤。我们逐步了解到需要签证才能旅行,慢慢掌握需要哪些文件、哪些表格需要填写及咋填表。当我们处理其中的一步,不会在脑海中保留整个过程的所有细节,而是专注当前任务。这与任务完成规模有关。真正标准是时间或进度安排、执行任务的精力、认知能力及它与任务熟悉度的关系,甚至包括执行这些任务的物理地点(如领事馆与照相馆等)。
软件开发亦是如此。虽然多年“瀑布式”开发流已广泛应用,但最终主要还是基于启发式和经验的估算技术(如规划扑克、T恤尺寸估算)及敏捷过程占据主导。正如在现实,我们尝试不过分细化规划整个过程,而是根据最新的进展来把握整体方向。
同样理念也适用于根据问题建模的软件。开始将它们拆分为不同应用程序,目的是更容易管理单个应用程序,更快开发和部署,并减少依赖关系,最后还带来更多技术选择的自由。没有一套完整流程适用于所有情况。我们会查看各部分,并认识到我们在设计模式或技术方面的集体经验,努力选择最佳方案并应用。
一种用于理解和解决软件设计复杂性的技术是领域驱动设计(DDD)。DDD提倡基于业务现实建模,并与我们的用例相关。随其热度下降,很多人可能忘记DDD方法实际上在理解当前问题并设计软件以达成对解决方案的共同理解方面非常有帮助。构建应用程序时,DDD将问题描述为领域和子领域。
它会将独立的步骤或问题领域定义为限界上下文。强调使用通用语言讨论问题,并引入许多技术概念,如实体、值对象和聚合根规则来具体实现。有时这些技术规则被视为实现DDD的硬性障碍,但最终,人们往往忘记最重要的是将代码工件与业务问题保持一致,并使用相同的通用语言。
设计为服务应用程序的限界上下文
我想讨论的架构风格与微服务相似。它涉及将单体应用程序拆分为多个独立的服务应用程序,或从一开始就借助限界上下文这一DDD的概念来单独开发它们。
有许多资源突出更细粒度服务的优点,作为微服务叙述的一部分。越来越多博客讨论在向细粒度服务过渡前或过渡期间你应该具备的防护网和可能遇到的陷阱。我将尝试不重复微服务的好处或为迁移到这种架构而需要的支持元素,而是重点讨论如何通过应用领域驱动设计的概念来更好地分离这些服务。
实例
一个借记/信用卡收单领域。这个领域可以(如很多情况下的那样,不幸地)被实现为一组单体应用程序。之所以有多个应用程序,仅因在不同应用程序中存在一些硬性技术限制(如希望执行批处理过程)。
大多数成功的架构都认识到通过DB集成是一种不好做法,因为它模糊了技术应用程序和业务职责之间的边界,使业务逻辑泄露到DB,并通过增加应用程序服务器数量来阻碍水平扩展。因此,向更好架构的演进表现为单体应用程序的服务集成。
现在,应用程序之间的边界变得更清晰了。但正如你所见,仍然存在隐藏的DB交互,这次是在各独立应用程序内部发生。我称它们为隐藏的,因为它们通常一开始很难被注意到。随时间推移,代码缠绕会使原本分离的业务流程在人为上联系,并在业务开发引入更多摩擦,因为这种共存需要共同部署独立的功能,可能减缓开发进度。
若你有一个领域模型指导你,领域建模有助识别和分离纠缠在一起的实现。若你还没现有应用程序的领域模型(这在大多数情况下是真实的),与其通过代码来理解不同职责,不如建立一个领域模型并将其映射到手头的应用程序,可能是更好方法。这不仅节省时间,还可避免陷入细节困境。而且,若业务团队与开发团队存在差距(可能是领域模型一开始就不存在的主要原因),讨论领域模型并将其与现有应用程序的功能对应起来,有助于弥补这一差距。
设计演进的下一步是将领域边界分离反映到我们的架构及限界上下文中。一个领域有多个限界上下文,意味着同一领域内可能有多个服务应用程序。
通过合适的领域模型,潜在的拆分点更加清晰,使我们能从更细粒度的应用程序中受益,如独立发布和版本控制、纯粹的基于能力的服务端点等,大多数这些优点已经在微服务文章中讨论过了。虽然许多关于微服务的讨论集中在技术中立性和开发纪律(避免/打破单体)上,但对于我们大多数人所处理的应用程序而言,领域和设计层面的考虑同样具有很高的价值。在转向微服务架构(借助领域模型)之后,DDD和更细粒度的服务可以相互协作,共同支撑。
这也将为团队提供一定程度的独立性,更精细的服务能力和更解耦的交互,如许多微服务文章中所解释的那样。
此外,正如案例信用卡支付收单领域中所见,这不是我们服务可达到的最细粒度分离。相反,这是通过我们的领域知识指导下的最有意义的分离。关键不在于服务的规模,而在于业务能力的划分。我认为这就是许多人所说的“正确的SOA”。