前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《架构整洁之道》第 25 章 层次与边界

《架构整洁之道》第 25 章 层次与边界

原创
作者头像
巴啦啦的积累
发布2023-06-13 09:56:58
1830
发布2023-06-13 09:56:58
举报
文章被收录于专栏:巴啦啦的积累巴啦啦的积累

人们习惯将系统分为三个组件:UI业务逻辑和数据库。对于一些简单的系统来说,三个就够了,但是稍微复杂一点的系统组件就不止这三个了。

以一个简单的游戏为例,粗看似乎也符合三个组件的架构设定。

首先UI接收用户输入数据,然后将数据传输给业务逻辑,最后数据入库。但仅仅只是这样吗?

基于文字的冒险游戏:Hunt The Wumpus

文字游戏,输入一些命令,游戏会返回对应的场景和执行动作。

现在决定包留这种基于文本的UI,但是要将UI和游戏业务逻辑之间的耦合解开,以便在不同地区使用不同的语言。

也就是说游戏业务逻辑UI的交互,不会使用自然语言,UI会将游戏业务逻辑传回的数据,转换成对应的自然语言。这就能做到多套UI可以复用同一个业务逻辑,而游戏的业务逻辑组件也不需要知道UI使用的是哪个语言。

业务逻辑(Game Rules),处理完UI给的数据后,就需要将数据存储了,但是我们不希望它只依赖某一种存储介质,所以让其存储组件依赖业务逻辑组件,遵守依赖关系原则。

可否采用整洁架构

这里我们只将它划分出来了组件,但是还没有找到所有的架构边界。

比如,语言并不是UI唯一的变动理由,因为我们还可能改变输入方式,例如,短信,Web,命令行,或者聊天程序。

这就意味着这类输入方式的变更,也应该需要一个对应的架构边界,需要构造一个API,以便将语言部分和输入方式部分隔离开。

这里的虚线,代表的是抽象组件(Boundary多态接口),具体实现类都是实线框。它们定义的API通常需要上下游组件来实现。

如果我们查看内部源码,会发现如下依赖关系。

比如Language组件的部分API是由EnglishSpanish组件来实现的。GameRules组件的部分API是由Language组件实现的。即,这些API的定义和维护是由使用方来定义和维护的,而非实现方。(被依赖被调用方只定义,调用方使用方负责实现和通信内容)。你想传什么给我,由你说。

TextDelivery使用的Boundary多态接口,由Language来定义实现,Language使用的Boundary多态接口,也有由TextDelivery来定义实现。

在这所有场景中,由Boundary接口所定义的API都是由使用者的上一层组件负责维护的。

如果我们去掉具体的实现类,只保留接口组件依赖结构进行简化,可以得到下面这张组件依赖图。

可以看到,所有的依赖都是向上的,很好的反映了GameRules作为最高策略组件的事实。

信息流方向:

来自用户的所有信息,都会通过TextDelievery组件传入。当信息流转到Language组件时,就会转换为具体的命令输入给GameRules组件,之后GameRules组件会将数据发送个DataStorage组件,接下来GameRules会将输出传递到Language组件,Language组件转换为合适的语言并通过TextDelievery将语言传递给用户。

在虚线左边的数据流关注用户的通信,右侧数据流关注数据的持久化。两边的数据流在顶部的GameRules汇聚,它是所有数据流的最终处理者。

交汇数据流

那么是不是意味着永远只有这两条数据流呢?当然不是的,如果这个游戏变成联机游戏,将会有三条。由此可见,随着系统的进化,组件在架构中自然会分裂出多条数据流来。

数据流的分隔

但在现实中,不会所有的数据流都最终会汇聚到一个组件上。

Hunt The Wumpus这个游戏中,有部分业务逻辑是处理玩家在地图中的行走,GameRules组件需要知道游戏中的洞穴如何相连,每个洞穴都存在什么物品,如何将玩家从一个洞穴转移到另一个洞穴,如何触发各类游戏事件等。

但是在游戏中,还有一个更高层次的策略,这个策略负责了解玩家的血量以及每个事件的后果和影响。这些策略会让玩家掉血或者加血。低层次的策略,负责向高层次的策略传递事件,例如FoundFoodFellInPit。高层次策略则要管理玩家的状态,最终该策略会决定玩家在游戏中的输赢。

以上是否属于架构边界呢?是否需要设计一个API来分隔MoveManagementPlayerManagement呢?回答这些问题前,我们可以把问题弄得更有意思点,加上微服务。

假定这个游戏面向海量用户,MoveManagement组件是运行在用户的本地计算机上,而PlayerManagement则部署在服务端处理,并为MoveManagement提供一个微服务API

在下图中,为该游戏绘制一个简化版的设计图。图中可以看到MoveManagementPlayerManagement有一个完整的系统架构边界。

本章小结

这个游戏很小只有几百行代码。举这个例子的目的在于证明架构边界可以存在任何地方。作为架构师我们必须小心审视什么地方才需要设计架构边界,另外还必须知道这个边界将会带来多大的成本。

作为架构师,我们应该怎么办?这个问题恐怕没有答案。因为一些很聪明的人一直在呼吁,不应该将未来的需求抽象化,YAGNI,You aren't going to need it,臆想中的需求事实上往往不存在,因为过度设计往往比设计不足还糟糕。但另一方面,如果我们需要设置边界,而没有设置边界,等到后面再去添加边界时,成本和风险往往会很高。

现实就是这样,我们必须要有一点未卜先知的能力,有时候要用脑子去猜。软件架构师必须要权衡成本和风险,决定哪里需要设计边界,哪些是完整边界,哪些是不完全边界,还有哪些是可以忽略的。

并且这不是一次性的工作,架构师必须持续观察系统的演进,时刻注意边界设计。然后权衡设置边界成本与不设置的成本。当设置边界的优势超过了不设置时,就是设置边界的最佳时期。

持之以恒,一刻也不能放松。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基于文字的冒险游戏:Hunt The Wumpus
  • 可否采用整洁架构
  • 交汇数据流
  • 数据流的分隔
  • 本章小结
相关产品与服务
短信
腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档