第三十三章 案例研究:视频销售
现在是时候把这些关于架构的规则和思想融合在一起,成为一个案例研究。这个案例研究将简短而简单,但将描述一个优秀的架构师所使用的流程以及这样的架构师所做出的决定。
产品
对于这个案例研究,我选择了一个我非常熟悉的产品:销售视频的网站的软件。当然,这让人联想到cleancoders.com,这是我销售我的软件教程视频的网站。
基本的想法是不重要的。我们有一批我们想销售的视频。我们通过网络将它们出售给个人和企业。个人可以支付一个价格观看流视频,而用另一个更高的价格才能下载这些视频,永久拥有它们。用于商业的许可只有流媒体,并且批量购买一定数量有折扣。
个人通常既是观众又是购买者。相比之下,企业经常会有人购买其他人会观看的视频。
视频作者需要提供他们的视频文件,书面描述和辅助文件,包括考试,问题,解决方案,源代码和其他资料。
管理员需要添加新的视频系列,添加和删除系列视频,并为各种许可确定价格。我们确定系统初始架构的第一步是确定角色和用例。
用例分析
图33.1展示了一个典型的用例分析
图33.1 一个典型的用例分析
四个主要行为者是显而易见的。根据单一责任原则,这四个参与者将是该体系的四个主要变化来源。每次添加一些新功能或者改变一些现有功能时,将采取该步骤来为这些参与者之一提供服务。因此,我们想要对系统进行划分,以便对一个参与者的改变不会影响其他参与者。
图33.1所示的用例不是一个完整的列表。例如,你不会找到登录或注销用例。这个遗漏的原因就是限制本书中的问题的大小。如果我要包括所有不同的使用案例,那么本章将不得不单独变成一本书。
注意图33.1中心的虚线用例。它们是抽象的用例。一个抽象的用例是一种设定,另一个用例将会出现的一般策略的用例。正如你所看到的,查看目录作为查看者和查看目录作为采购者用例都从查看目录抽象用例继承。
一方面,我不是完全有必要创建抽象的。我可以将抽象用例从图表中排除出去,而不会影响整个产品的任何特性。另一方面,这两个用例是如此相似,以至于我认为在分析的早期就认识到相似性并找到一种统一方法是明智的。
组件架构
现在我们知道了角色和用例,我们可以创建一个初步的组件架构(图33.2)。
图中的双线代表像往常一样的架构边界。您可以看到典型的分区:视图,展示器,交互器和控制器。你也可以看到,我已经打破了他们相应的角色的每个类别。
图33.2中的每个组件代表一个潜在的.jar文件或.dll文件。每个组件都将包含已分配给它的视图,展示器,交互器和控制器。
请注意Catalog View 和Catalog Presenter的特殊组件。这就是我处理抽象View Catalog用例的方法。我假设这些视图和展示器将被编码到这些组件中的抽象类中,并且继承组件将包含将从这些抽象类继承的视图和展示器类。
图33.2初步组件架构
我真的将系统分解成所有这些组件,并将它们作为.jar或.dll文件提供吗?是和不是。我肯定会以这种方式打破编译和构建环境,这样我就可以构建独立的可交付成果。如有必要,我也保留将所有可交付成果合并到少量交付成果中的权利。例如,给定图33.2中的分区,可以很容易地将它们组合成五个.jar文件,分别用于视图,展示器,交互器和控制器和实用程序。然后,我可以独立部署最可能彼此独立更换的组件。
另一种可能的分组方式是将视图和展示器放在一起放入同一个.jar文件,并将交互器、控制器和实用程序放在它们自己的.jar文件中。还有一个更原始的分组就是创建两个.jar文件,其中一个文件中包含视图和展示器,另一个文件中包含其他内容。
保持这些选项的开放性将使我们能够根据系统随时间的变化来调整我们部署系统的方式。
依赖管理
图33.2中的控制流程从右向左进行。输入发生在控制器上,输入由交互器处理成结果。展示器然后格式化结果,视图显示这些展示内容。
注意箭头并不全都从右向左流动。其实大部分都是从左到右的。这是因为架构遵循依赖规则。所有的依赖关系沿着一个方向跨越边界线,并且总是指向包含更高级别策略的组件。
另请注意,使用关系(空心箭头)指向于控制流方向一致,而继承关系(闭合箭头)指向与控制流相反。这描述了我们使用开闭原则确保依赖关系朝着正确的方向流动,并且对低层细节的更改不会向上蔓延,影响高层政策。
小结
图33.2中的体系结构图包括两个维度的分离。首先是基于单一责任原则的角色的分离;第二个是依赖规则。两者的目标是分离出因不同原因和不同速率而变化的组件。不同的原因对应于角色;不同的变化速率对应不同的策略层级。
一旦以这种方式构建了代码,就可以混合和匹配如何实际部署系统。你可以以任何有意义的方式将组件分组为可部署的交付物,并且可以在情况发生变化时轻松更改该分组。