T31架构设计项目
day1:2021-10-27
一、项目介绍
T31项目是类似于12306的售票网站
- 从查票、下单、付钱、通知的主流程
- 抽象商品、订单、支付的核心模型
- 处理票务异常和日志
- 了解架构设计背后的方法论
- 以战促练,全面提升代码能力、设计能力、交付能力和协作能力。
二、需求分析
核心:用户的诉求
理解和挖掘用户的诉求、以及背后的逻辑,转换成可行性的分析结果。从非结构化到结构化,确定系统的职责、模块的过程。
分析需求的内容
-
项目 / 需求边界
- 让研发、QA能够相对准确的在MRD阶段评估时间;
- 确定产品的适用范围,性能要求,做到对产品的能力范围心中有数,也有优化方向。
-
用户故事
- “ 用户故事” 是一份简要的意向声明,描述了为用户执行的操作的系统需要;
- 用户故事是一种工具, 用于以开发人员和用户都能理解的方式定义系统的行为。用户故事将工作重点放在用户定义的价值上, 而不是功能分解结构上;
- 例子:查看航班信息、查看检查我当前的电费计费率。
-
用户路径
- 用户路径分析可以用来衡量网站优化的效果或营销推广的效果,以及了解用户行为偏好,其最终目的是达成业务目标,引导用户更高效地完成产品的最优路径,最终促使用户付费;
- 所以对于路径的使用方式,要尽可能地简单,方便。
-
分析背后的人性:人性是提出需求的本源
-
需求商品化:模块化、配置化、有逻辑
-
需求落地路径:需求分析 -> 可行性 -> 设计 -> 编码 -> 测试 -> 发布
分析需求的必要性
伪需求:没有调研、没有目标、没有逻辑的无脑需求
应对措施
- 用数据化结果否定需求合理性;
- 用正反案例来说明需求需要改进的地方;
- 用户路径和储点推演需求合理性。
权力需求:老板或是强势业务方的需求
应对措施
- 先肯定需求价值再提出需求实现的成本;
- 给出更好的需求替代方案;
- 从数据和案例角度说明需求快速上线危害性。
项目需求分析
- 用户通过网站注册并登录
- 车次、车厢、经停站、时刻表正删改查
- 修改个人信息
- 乘客管理
- 余票查询
- 创建(票)订单
- 第三方支付(支付宝)
- 支付成功通知(MOCK)
三、问题的分层
- 本源问题——用户问题:“我想支付”
- 经营视角——业务问题:支持一切可以支付的第三方支付工具
- 体系结构——产品问题:支付需要逆向流程、异常流程、对账模块等
- 架构代码——技术问题:高并发、可用性、实现第三方支付的链路
KISS原则
架构的理念是大道至简:解决问题
- 如何让我们的系统有可扩展性和可维护性
- 如何让我们的系统能够恰到好处地解决问题
- 如何让我们的系统能够运行3-5年不重构
现代软件需要很强的协调能力,保持微笑,迎接各种否定
- 业务部门的挑战——价值问题
- 成本部门的挑战——ROI问题
- 测试部门的挑战——可测试性
- 技术部门的挑战——可行性和工期
DRY原则
Don’t Repeat yourself
一切重复的代码都可以抽象
重复代码的危害性:
- 不一致性
- 代码冗余
- 易出Bug
四、设计原则
七大设计原则
-
共同点
- 提升软件的可扩展性、可维护性、是抽象思维和归纳思维的集中体现。
-
实践点
- 类图设计
- 接口设计
- 组合设计
七大设计原则内容
1.单一职责原则(SRP:Single responsibility principle)
-
别名:单一职责原则又称单一功能原则。
-
核心:解耦和增强内聚性(高内聚,低耦合)。
-
描述:
- 类被修改的几率很大,因此应该专注于单一的功能。如果你把多个功能放在同一个类中,功能之间就形成了关联,改变其中一个功能,有可能中止另一个功能,这时就需要新一轮的测试来避免可能出现的问题。
2.里氏替换原则(LSP:Liskov Substitution Principle)
-
核心:
- 在任何父类出现的地方都可以用他的子类来替代(子类应当可以替换父类并出现在父类能够出现的任何地方)。
-
四层含义:
- 子类必须完全实现父类的方法。在类中调用其他类是务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则;
- 子类可以有自己的个性。子类当然可以有自己的行为和外观了,也就是方法和属性;
- 覆盖或实现父类的方法时输入参数可以被放大。即子类可以重载父类的方法,但输入参数应比父类方法中的大,这样在子类代替父类的时候,调用的仍然是父类的方法;
- 即以子类中方法的前置条件必须与超类中被覆盖的方法的前置条件相同或者更宽松;
- 覆盖或实现父类的方法时输出结果可以被缩小。
3.依赖注入原则(DIP:Dependence Inversion Principle)
-
别名:依赖倒置原则或依赖反转原则。
-
核心:要依赖于抽象,不要依赖于具体的实现。
-
三层含义:
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象(抽象类或接口);
- 抽象不应该依赖细节(具体实现);
- 细节(具体实现)应该依赖抽象。
-
三种实现方式:
- 通过构造函数传递依赖对象 ;
- 通过setter方法传递依赖对象;
- 接口声明实现依赖对象 在Java中的表现:
- 模块间的依赖是通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;
- 接口或抽象类不依赖于实现类;
- 实现类依赖接口或抽象类。
4.开闭原则(OCP:Open Closed Principle)
-
核心思想
- 对扩展开放,对修改关闭。即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。
- 根据开闭原则,在设计一个软件系统模块(类,方法)的时候,应该可以在不修改原有的模块(修改关闭)的基础上,能扩展其功能(扩展开放)。
-
扩展开放
- 某模块的功能是可扩展的,则该模块是扩展开放的。软件系统的功能上的可扩展性要求模块是扩展开放的。
-
修改关闭
- 某模块被其他模块调用,如果该模块的源代码不允许修改,则该模块修改关闭的。软件系统的功能上的稳定性,持续性要求是修改关的。
为什么系统需要扩展性?
熵增定律
熵:反应自发过程不可逆性的物质状态指标,熵是衡量我们这个世界中事物混乱程度的一个重要指标。一个孤立系统总是存在从有序度转变成无序的趋势,这就是熵增定律。
开闭原则是熵减
对抗熵增的最好方式就是熵减。熵减最重要的是形成开放性;
开放性设计是系统能够有序接纳外部能量的能力。在软件领域,我们的外部能量指的是需求的多变 和业务的不断试错。扩展性设计是一种开闭原则思维,比如项目中支付我们未来支付VISA,MASTER,或者是APPLE PYA。通知未来支持电话语音通知,钉钉等各类办公软件通知等。
5.接口分离原则(ISP:Interface Segregation Principle)
-
核心思想
- 不应该强迫客户程序依赖他们不需要使用的方法;
- 接口分离原则的意思就是:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一个接口当中。
-
分离接口的两种实现方法
- 使用委托分离接口。(Separation through Delegation);
- 使用多重继承分离接口。(Separation through Multiple Inheritance)。
6.合成复用原则(CRP:Composite Reuse Principle)
-
核心思想
- 尽量使用对象组合,而不是继承来达到复用的目的。该原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分:新的对象通过向这些对象的委派达到复用已有功能的目的。
-
复用的种类
- 继承
- 合成聚合
注:在复用时应优先考虑使用合成聚合而不是继承
7.迪米特原则(LOD:Law of Demeter)
又叫最少知识原则
-
核心思想
- 一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。
- (类间解耦,低耦合)意思就是降低各个对象之间的耦合,提高系统的可维护性;在模块之间只通过接口来通信,而不理会模块的内部工作原理,可以使各个模块的耦合成都降到最低,促进软件的复用。
-
类与类,类和接口,接口和接口之间的关系。
- 实现关系(一个类实现一个接口)
- 泛化关系(一个类继承另一个类)
五、架构与架构图
什么是架构
架构是一种能力,而不是一个职位
-
架构 = 组成 + 决策
-
组成 = 模块结构 + 模块关系
-
决策 = 约束 + 设计原则 + 演化方向
架构的目的
- 确定系统边界,在技术层面上做与不做;
- 确定系统里各模块之间的依赖关系与模块的宏观输入与输出;
- 使后续的子系统或模块设计在一个既定的框架内和技术方向熵继续演化;
- 明确非功能性需求,非功能性需求是指安全性、可用性、可扩展性等 。
如何画出架构图
架构图:水平层面的业务模块 + 垂直层面上的技术模块 依赖形成的图
-
搞清楚要画的架构图的类型
-
确认架构图中的关键要素(比如产品、技术、服务)
-
梳理关键要素之间的关联
- 包含
- 支撑
- 同级并列等
-
输出关联关系清晰的架构图
架构图的好与坏
- 布局:图形的上下左右前后6个方向的位置关系
- 颜色:视觉中心在哪里,哪些元素被忽略
- 逻辑:组件之间的依赖,业务实现的可行性
架构图的分类
- 业务架构:使用一套方法论,对所涉及到的业务单元进行边界划分熟悉业务
- 购物网站系统 -> 商品类目,订单服务,支付,退款等功能进行清晰划分
- 应用架构:对整个系统的实现进行可视化落地实践,系统的层次 / 开发原则 / 各个层次的应用服务,一般为垂直依赖
- 购物网站系统 -> 数据层,服务层,通讯层,展现层
- 数据架构:是一套对存储数据的框架逻辑,根据各个系统应用场景、不同时间段的应用场景,对数据进行诸如数据异构、读写分离、缓存使用、分布式数据策略等划分
- 技术架构:承接应用架构的技术需要,并根据识别的技术需求,进行技术选型,把各个关键技术和技术之间的关系描述清楚
传统架构图
- 物理视图:和部署相关的架构决策 —— 一般不画
- 逻辑视图:设计满足功能需求的架构 —— 逻辑结构图
- 开发视图:设计满足开发期质量属性的架构 —— UML图
- 处理视图:设计满足运行期质量属性的架构 —— UML图
- 场景视图:需求分析技术,通常采用UML的用例图进行设计
UML
定义
Unified Model Language
- Unified:说明以前不统一
- Model:建模往往需要抽象
- Language:交流,为什么能交流,定义共同的协议
分类
统一建模语言,使用图形和符号描述软件模型中的各个元素和它们之间的关系
UML的分类
-
静态结构图
- 类图、对象图、包图、组件图、部署图
-
动态行为图
- 交互图(时序图与协作图)、状态图、活动图
类的六大关系
- 泛化关系:即继承关系
- 实现关系:实现接口
- 聚合关系:业务上整体与部分可以分开,是关联关系的特例
- 组合关系:业务上整体与部分不可分分开,同样是关联关系的特例
- 依赖关系:只要在类中用到了对方,就存在依赖关系
- 关联关系:体现的是业务逻辑的关系,是依赖关系的特例,具有导航性和多重性
类图
类图(Class diagram)是显示了模型的静态结构,特别是模型中存在的类、类的内部结构以及它们与其他类的关系等。
- 项目核心:票
- 核心服务:订单
时序图
通过描述对象之间发送消息的时间顺序显示多个对象之间的动态协作
-
关注正常流程
-
不关注逆流程
-
不关注异常流程
-
不关注分支判断
架构图
架构图是什么?
架构图则是表达这种集合的载体,是水平的业务单元 + 垂直的技术单元组成的逻辑结构图
架构图的作用是什么?
将目标系统的结构可视化,通过直观的方式描述技术思维,减少沟通障碍,提升协作效率,划分目标系统的边界。