写点什么

架构师训练营第十章总结

用户头像
叮叮董董
关注
发布于: 2020 年 08 月 11 日

一、组件模块

软件的复杂度和它的规模成指数关系。


一个复杂度为 100 的软件系统,如果能拆成互不相关、同等规模的子系统,那么每个子系统的复杂度是 25,而不是 50。软件开发这个行业很久之前就形成一个共识,应该将复杂的软件系统进行拆分,拆分多个复杂度更低的子系统,子系统还可以拆分为更小粒度的组件。


那我们在拆分的时候有哪些原则?


1、组件内聚原则


组件内聚原则主要讨论哪些类应该聚合在同一个组件,以便组件既能够提供相对完成的功能,又不至于太过庞大;


2、复用发布等同原则


软件复用的最小粒度应该等同于其发布的最小粒度。也就是说,如果希望别人以怎样的粒度复用你的软件,你就应该怎么样的粒度发布你的软件。组件是软件复用和发布的最小粒度软件单元。这个粒度即是复用的粒度,也是发布的粒度。


版本号约定建议:


  • 版本号格式:主版本号.此版本号.修订号。比如 2.5.1,这个版本中,主版本号是 2,次版本号是 5,修订号是 1;

  • 主版本号升级,表示组件发生了不向前兼容的重大修订;

  • 此版本号升级,表示组件进行了重要的功能修订或者 bug 修复,但是组件是向前兼容的;

  • 修订号升级,表示组件进行了不重要的功能修订或者 bug 修复。


3、共同封闭原则


应该将同时修改,并且为了相同目的而修改的类放到同一个组件中。而将不会同时修改,并且不会为了相同的而修改的类放到不同的组件中。


组件的目的虽然是为了复用,然而开发中常常引发问的,恰恰在于组件本身的可维护性。如果组件在自己的生命周期中必须经历各种变更,那么最好不要设计其它组件,相关的变更都在同一组件总。这样,发生变更的时候,只需要重新发布这个组件就可以了,而不是一大堆组件都受到牵连。


4、共同复用原则


这个原则是说,不要强迫一个组件的用户依赖他们不需要的东西。


一方面,我们应该将相互依赖、公共复用的类放在一个组件中。另一方面,这个原则也说明,如果不是被公共依赖的类,就不应该放在同一个组件中。如果不被依赖的类发生变更,就会引起组件变更,进而引起使用组件发生变更。这样就会导致组件的使用产生不必要的困扰,甚至讨厌使用这样的组件,也造成了复用的困难。


5、无循环依赖原则


组件依赖关系中不应该出现环。如果组件 A 依赖组件 B,组件 B 依赖组件 C,组件 C 又依赖组件 A,就形成了循环依赖。


很多时候,循环依赖时在组件的变更中逐渐形成的,组件 A 版本 1.0 依赖组件 B 版本 1.0,后来组件 B 升级到 1.1,升级的某个功能依赖组件 A 的 1.0 版本,于是形成了循环依赖。如果组件的设计边界不清晰,组件开发的设计缺乏评审,开发者只关注自己开发的组件,整个项目对组件依赖管理没有统一的规则,很有可能出现循环依赖。


6、稳定依赖原则


组件依赖关系必须向更稳定的方向。很少有变更的组件是稳定的,也就是说,经常变更的组件是不稳定的。根据稳定的依赖原则,不稳定的组件应该依赖稳定的组件,而不是反过来。


反过来说,如果一个组件被更多的组件依赖,那么它需要相对稳定的,因为想要变更一个被很多组件依赖的组件,本身就是一件困难的事。相对应的,如果一个组件依赖了很多的组件,那么它相对也是不稳定的,因为它依赖的任何组件变更,都可能导致自己的变更。


7、稳定抽象原则


一个组件的抽象化程度应该与其稳定性程度一致。也就是说,一个稳定的组件应该是抽象的,而不是稳定的组件应该是具体的。


这个原则对具体开发的指导意义就是:如果你设计的组件是具体的、不稳定的,那么可以为这个组件对外提供服务的类设计一个接口,并把这组接口封装在一个专门的组件中,那么这个组件就相对比较抽象、稳定。


那我们在设计的过程中应该遵循怎样的一个原则?


最著名的就是倒三角原则,在微服务的场景中特别重要,在架构设计的过程中最重要的就是 Needs 需求,首先要理解需求是什么,要解决什么问题?有了这个需要看看这个方案和架构能不能带来 Values(价值),如果能带来价值,使用这种方案需要用什么 Principles(原则),在原则之下我们要知道 Practices(最佳实践)是那些,最好好这个架构技术选型需要那种 Tools(工具),包括基础框架、设计等框架。


二、微服务

巨无霸应用系统带来的问题

1)编译、部署困难。

2)代码分支管理困难,merge 冲突,发布的时候问题互相纠缠,顾此失彼,每次发布都要半夜三更。

3)数据库连接耗尽。

4)新增业务困难。都是雷区,老人忙得要死,新人一帮忙就容易添乱。



大系统拆分方式

纵向拆分:将一个大应用拆分成多个小应用,如果新增业务较为独立,那么就直接将其设计部署为一个独立的 Web 应用系统。

横向拆分:将复用的业务拆分出来,独立部署成微服务,新增业务只需要调用这些微服务即可快速搭建一个应用系统。



Web Service 分布式服务


缺点:

1)臃肿的注册与发现机制

2)低效的 XML 序列化

3)开销相对高的 HTTP 网络通信

4)复杂的部署和维护手段



Dubbo 微服务框架架构


Service Mesh 服务网格


Service Mesh 是一个基础设施层,用于处理服务间的通信,通常表现为一组轻量级网络代理,它们与应用程序部署在一起,而对应用程序透明。



Service Mesh 的 sidecar 模式



微服务架构落地

  • 业务先行,先理顺业务边界和依赖关系(最重要),技术是手段而不是目的。

  • 先有独立模块,后又分布式服务。

  • 业务耦合严重,逻辑复杂多变的系统进行微服务重构要谨慎。

  • 要搞清楚实施微服务的目的是什么?业务复用?开发边界清晰?分布式集群提升性能?



微服务最佳实践

  • CQRS:读命令与写命令隔离。

  • 事件溯源:将用户请求过程中每次状态变化都记录到事件日志中。

  • 断路器:三种状态是关闭、打开、半开。


  • 服务重试与调用超时:上游调用者超时时间要大于下游调用者超时时间之和。

  • 最重要的是需求:


三、DDD


为什么需要 DDD?

很多项目的实际情况:

  • 产品经理需求零零散散,不断变更。

  • 工程师在各处代码中寻找可以支持需求变更的代码,反复修修补补,对每块代码的职责缺乏思考。

  • 软件只有需求分析,缺乏真正的设计,系统没有一个统一的领域模型维持其内在逻辑一致性。(产品堆功能,但是对于这个功能是否真的是这个领域所必要的,缺乏思考)

  • 功能特性并不是按照领域模型内在的逻辑设计,而是按照人自己的主观想象拍脑袋设计。



项目时间一长,各种困难重重,需求延期,线上 bug 不断,管理者考虑要不要推倒重来,程序员考虑要不要跑路。



贫血模型 VS 充血模型

贫血模型:类似 Controller、Service、Dao 这种这种只有方法,没有成员变量,而方法调用时传递的数值对象,比如 Contract,没有方法(或者只有构造方法、getter、setter),典型的面向过程。

充血模型:合并了行为和数据的领域对象模型。通过领域模型对象的交互完成了业务逻辑。也就是说,设计好了领域模型对象,也就设计好了业务逻辑实现。



什么是领域?

领域是一个组织所做的事情以及其包含的一切,通俗地说,就是组织的业务范围和做事方式,也是软件开发的目标范围。领域驱动设计就是从领域出发,分析领域内模型及其关系,进而设计软件系统的方法。


什么是子域?

领域是一个组织所做的事情及其包含的一切。这个范围就太大了,不知道该如何下手。所以通常的做法是把一个领域拆分成多个子域。比如:用户、商品、订单、库存、物流、发票等。(一个微服务很可能就是一个子域)如何划分子域是一个关键。(卖家提现功能属于用户子域,还是财务子域?)



限界上下文

在一个子域中,会创建一个概念上的领域边界,在这个边界中,任何领域对象都只表示特定于该边界内部的确切含义。这样边界便称为限界上下文。限界上下文和子域具有一对一的关系,用来控制子域的边界,保证子域内的概念统一性。通常限界上下文对应一个组件或者一个模块,或者一个微服务,一个子系统。



上下文映射图

不同的限界上下文,也就是不同的子系统或者模块之间会有各种交互动作。DDD 使用上下文映射图来设计这种关联和交互。


DDD 分层架构

领域实体的组合调用和事务控制在应用层。


DDD 的战略设计和战术设计

领域、子域、限界上下文、上下文映射图,这些是 DDD 的战略设计。

实体(领域模型对象,每个实体是唯一的,有唯一标识。比如一个订单对象是一个实体。实体会发生变化,但是实体的唯一标识不会变化)、值对象(并不是领域内的对象都应该被设计为实体,比如住址的对象就是值对象,值对象不会变化,也没有行为)、聚合(是一个关联对象的集合,我们将其作为一个单元来处理数据更改。聚合根:将多个实体和值对象聚合在一起的实体)、CQRS、事件溯源,这些是 DDD 战术设计。

通过战略设计,划分模块和服务的边界及依赖关系,对微服务架构的设计至关重要。



用户头像

叮叮董董

关注

还未添加个人签名 2020.04.08 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营第十章总结