模块划分是这样做吗?
你们有没有这样的苦恼,当我们自己想设计一个基础框架的时候需要做模块划分,但是该怎样去划分模块?先简单的说说大众所想的微服务框架模块划分;
一般的设计思路:
-
先确定基础框架,比如SpringBoot/Dubbo/ServiceComb,然后经过调研对比选择合适的版本;
-
选择微服务中间件注册中心、配置中心、缓存、网关等一些列组件;
-
然后集成基础功能组件,比如数据库访问、事务、代码生成、异常处理、防重幂等等功能;
-
基于基础框架开发一些系统比如分布式锁、分布式缓存、异步服务等基础服务;
-
提供框架工具类,比如文件处理、格式化、日期等工具类;
-
开发完成之后做样例项目集成;
模块可能这样划分:
-
会确定一个统一管理三方库的模块,用于管理第三方组件库的版本;
-
会确定一个统一管理二方库的模块,用于管理框架相关的版本;
-
会把每个组件定义成一个大的模块,然后在细化成具体的模块,比如大模块XXX-cache,子模块则叫XXX-cache-core、XXX-cache-service等;
-
做开源二次开发的模块会单独定义成一个模块,比如配置中心扩展定义为XXX-config-file等;
-
会把基础功能独立完成的单独作为一个模块比如叫XXX-db-mybatis、XXX-utils等;
-
把其它公共的组件作为一个独立的模块,比如XXX-common;
-
最后新建一个样例模块,用于验证基础框架;
一个简单的框架模块就这样划分完成了,大概一看没什么毛病,可这其中的问题一大堆;我们看看正确的模块划分思路是怎么样的,首先我们得梳理清楚几个点;
1、确定用户,开发的这个框架是给谁用的,如果给开发人员用需要支持哪些功能,如果是给测试人员用,需要支持哪些功能,如果是给业务开发人员使用,有需要支持哪些功能;
2、框架怎么用,项目框架能不能灵活的解耦和组装,每个模块之间怎么交互,单元测试怎么做,代码怎么管理等;
3、框架的量级规模,这个框架能支持多大的用户数,每秒请求数等等;我们需要根据数据规模,确定数据是否需要分片、分库,同时也要确定中间件的选择,能否满足当前场景;
4、高性能,怎么提供集群方式,怎么扩容,怎么实现7*24小时不宕机,怎么实现灰度发布等功能;
软件的复杂度和它的规模成指数关系。一个复杂度为100的软件系统,如果能拆成互不相关、同等规模的子系统,那么每个子系统的复杂度是25,而不是50。软件开发这个行业很久之前就形成一个共识,应该将复杂的软件系统进行拆分,拆分多个复杂度更低的子系统,子系统还可以拆分为更小粒度的组件。
拆分的时候有哪些原则
01
组件内聚原则
组件内聚原则主要讨论哪些类应该聚合在同一个组件,以便组件既能够提供相对完成的功能,又不至于太过庞大;
02
复用发布等同原则
软件复用的最小粒度应该等同于其发布的最小粒度。也就是说,如果希望别人以怎样的粒度复用你的软件,你就应该怎么样的粒度发布你的软件。组件是软件复用和发布的最小粒度软件单元。这个粒度即是复用的粒度,也是发布的粒度。
版本号约定建议:
-
版本号格式:主版本号.次版本号.修订号。比如2.5.1,这个版本中,主版本号是2,次版本号是5,修订号是1;
-
主版本号升级,表示组件发生了不向前兼容的重大修订;
-
此版本号升级,表示组件进行了重要的功能修订或者bug修复,但是组件是向前兼容的;
-
修订号升级,表示组件进行了不重要的功能修订或者bug修复。
03
共同封闭原则应该将同时修改,并且为了相同目的而修改的类放到同一个组件中。而将不会同时修改,并且不会为了相同的而修改的类放到不同的组件中。
组件的目的虽然是为了复用,然而开发中常常引发问,恰恰在于组件本身的可维护性。如果组件在自己的生命周期中必须经历各种变更,那么最好不要设计其它组件,相关的变更都在同一组件。这样,发生变更的时候,只需要重新发布这个组件就可以了,而不是一大堆组件都受到牵连。
04
共同复用原则
这个原则是说,不要强迫一个组件的用户依赖他们不需要的东西。
一方面,我们应该将相互依赖、公共复用的类放在一个组件中。另一方面,这个原则也说明,如果不是被公共依赖的类,就不应该放在同一个组件中。如果不被依赖的类发生变更,就会引起组件变更,进而引起使用组件发生变更。这样就会导致组件的使用产生不必要的困扰,甚至讨厌使用这样的组件,也造成了复用的困难。
05
无循环依赖原则
组件依赖关系中不应该出现环。如果组件A依赖组件B,组件B依赖组件C,组件C又依赖组件A,就形成了循环依赖。
很多时候,循环依赖在组件的变更中逐渐形成的,组件A版本1.0依赖组件B版本1.0,后来组件B升级到1.1,升级的某个功能依赖组件A的1.0版本,于是形成了循环依赖。如果组件的设计边界不清晰,组件开发的设计缺乏评审,开发者只关注自己开发的组件,整个项目对组件依赖管理没有统一的规则,很有可能出现循环依赖。
06
稳定依赖原则?
组件依赖关系必须向更稳定的方向。很少有变更的组件是稳定的,也就是说,经常变更的组件是不稳定的。根据稳定的依赖原则,不稳定的组件应该依赖稳定的组件,而不是反过来。
反过来说,如果一个组件被更多的组件依赖,那么它需要相对稳定的,因为想要变更一个被很多组件依赖的组件,本身就是一件困难的事。相对应的,如果一个组件依赖了很多的组件,那么它相对也是不稳定的,因为它依赖的任何组件变更,都可能导致自己的变更。
07
稳定抽象原则?
一个组件的抽象化程度应该与其稳定性程度一致。也就是说,一个稳定的组件应该是抽象的,而不是稳定的组件应该是具体的。
这个原则对具体开发的指导意义就是:如果你设计的组件是具体的、不稳定的,那么可以为这个组件对外提供服务的类设计一个接口,并把这组接口封装在一个专门的组件中,那么这个组件就相对比较抽象、稳定。
设计的过程中应该遵循怎样的原则
最著名的就是倒三角原则,在微服务的场景中特别重要,在架构设计的过程中最重要的就是Needs需求,首先要理解需求是什么,要解决什么问题?有了这个需要看看这个方案和架构能不能带来Values(价值),如果能带来价值,使用这种方案需要用什么Principles(原则),在原则之下我们要知道Practices(最佳实践)是那些,最好好这个架构技术选型需要那种Tools(工具),包括基础框架、设计等框架。
有了这些基础原则的支撑我们是不是对模块设计又有了新的了解,没有最好的模块设计,只有符合公司业务需求模块的最佳实践。不能说这个框架满足什么需求,应该说这个需求被框架支撑着。所有很难出现一个基础框架满足所有需求,只能做各种适配,从而丢失了框架应该有的特性。