系统架构的模块化与组件化

系统架构的模块化与组件化

一、软件研发中的问题

我们都知道在做软件开发的时候,随着业务的不断扩充,功能的增加,系统会越来越复杂,代码的耦合度也会越来越高。
具体表现为:

  • 代码量膨胀,不利于维护,更不利于新功能的开发
  • 业务开发分工不明确,开发人员要关心非业务的代码
  • 不同业务代码耦合严重,难以多人合作,职责不分明
  • 改代码时,可能会影响其他业务,牵一发动全身

多人一起开发时,如果代码结构、模块化的不好,就很难对不同业务划分出分界线,难以明确各自的职责,牵一发动全身,出了问题更是容易相互扯皮(这个时候只能说一句“怪我咯o(╯□╰)o”),更不用提合并代码时的冲突了。

因此为了尽可能的减少规避这些问题,人们发明了不同开发框架,如MVP、MVC(TP框架)、MVVM等,这些都是为了降低研发成本。这些只是规范了代码,理论上大家严格遵守框架规范是可以做出单个可维护性较高的系统,但一个系统一定会越来越庞大,功能模块也会越来越多,单纯的依靠软件架构规范已经没办法避免以上这些问题了。
后来就诞生了模块化、组件化、微服务等一些拆分方案, 目的都是为了解耦代码,提高代码的复用率和容错率,以及提升研发效率。
具体优势如下:

  • 架构更清晰,解耦
  • 加快编译速度
  • 业务分工明确,开发人员仅专注与自己的业务
  • 提高开发效率
  • 组件、业务独立更新版本,可回滚,持续集成
    二、两者关系
    从上面的阐述可以得出,一个工程,由多个模块组成,每个模块由多个组件构成。但很多时候,两者界限还是相当模糊。例如“日志组件”称为“日志模块”,也没有违和感。
  • 组件从业务角度上不能继续拆分,可替换,可复用
    = 模块的定义比较笼统,可以是一个Business业务,可以是技术架构中一个业务,也可以是几个组件构成的小功能

无论是组件化还是模块化,目标都是把臃肿的工程,拆分为更小的部分,解耦各种复杂的逻辑,便于代码管理。

三、模块化与组件化
  1. 什么是模块化?
    模块翻译为“Module”,字面意思。模块由多个组件构成,它可以实现一个独立的功能,甚至业务。模块化的目的在于将一个程序按照其功能做拆分,分成相互独立的模块,以便于每个模块只包含与其功能相关的内容,模块之间通过接口(API或者RPC)调用。
    模块
    举例:“展翅网”的大模块有,首页-实习-兼职、技能培训、测评、吾聊职场都可以称为一个大模块;当然这样的拆分还不够精细,大家普遍称之为项目会更合适,为了更细分使模块复用率更高,我们必须继续拆分,使服务层和上层业务独立分开,这样我们才能更好的做系统模块化。


模块细化还要有一些基础模块,如下图

  1. 什么是组件化?

    组件,应该翻译为“Component”,意思是组件、部件、元件。在电子电路中,电子元件是电子电路中的基本元素。在App工程上,组件是构成业务或者功能模块的基本单位。原则上,组件与组件之间互不依赖。
    组件
    例如,图片上传功能,应该叫“图片上传组件”,而不是“图片上传模块”。因为图片上传从功能、业务上,已经不能往下拆了。图片上传可能使用七牛sdk,或者又拍云sdk。无论图片上传组件用七牛sdk,还是又拍云sdk,都不会影响这个组件的功能,因此组件具有可替换性;同时,图片上传组件可以被多个业务使用,因此组件具有可复用性。由于sdk只是技术细节,它跟业务并没有关系。在业务架构图上,也不会出现“xx sdk”,因此我们说图片上传组件不能拆分了。
    美团点评的前端组件框架

    从上图可以看出,多个组件构成一个模块,当模块相当于业务时,就是说该业务由多个组件组合而成。

  2. 组件化的优势

    1.可扩展性

    这里主要是讲前端为主,其实后端服务也是一样,比如爬虫模块的下载组件,这里不做具体的阐述

通过组件之间的合理组合搭配,可以构建出满足业务需求的新组件。
这一点很好理解,举个例子,我们有一个基础的树形组件treetree-node,现在来了一个业务需求,需要开发一个人员/机构的选择器,那么我们就可以很方便的基于我们的tree组件进行扩展,创建一个新的组件xx-selector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<template>
<list :data="selectedData" /> // 已选列表
</selected>
<tree :data="treeData" onSelect="onSelect">
<template slot="treeNode">
...custom ui here
</template>
</tree>
</template>
<script>
import tree from ./tree.vue
import list from ./list.vue
module.exports = {
data:{
treeData:[],
selectedData:[]
},
created:function(){
this.init()
}
methods:{
onSelect:function(selected){
this.selectedData = selected
this.$emit('onSelectedChange',selected)
},
initData:function(){
// ajax请求获取人员信息
}
}
}
</script>

2.可复用
刚才的xx-selector组件就复用到了基础的tree组件和tree-node组件。同样的道理,我们开发的xx-selector人员选择器组件,可以在任何有同样需求的地方重复使用,可以成为一个专属你们开发团队的一个基础组件,减少了开发量。

3.高内聚/低耦合
在我们使用xx-selector组件时,我们无需关心该组件内部的实现细节,我们只需要监听它暴露出来的onSelectedChanged(selectedList)事件,这个事件在选中人员发生改变时触发,把已选人员列表selectedList传递我们——我们使用选择器,所该关心就只是已选列表而已。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<xx-selector onSelectedChanged="selectorChanged">
</template>
<script>
import xx-selector from ./xx-selector.vue
module.exports = {
data:{ },
methods:{
selectorChanged:function(selected){
// 处理已选
}
}
}
</script>

这样就实现了

  • 组件内部高度内聚——只给外部提供功能,对外部的修改关闭
  • 组件之间低耦合——组件与组件只需要监听事件、触发事件,子组件不依赖与父组件
王洋 wechat
我的微信号,欢迎交流~

热评文章