模块划分是架构师的"看家本领"——通过系统化的方法将复杂系统分解为可管理的模块,实现关注点分离、模块复用和深度优先开发。正如Daniel D. Gajski所说:"系统设计中一个最重要的和最具有挑战性的任务就是…将系统所要执行的功能分到各个组件上。"本文通过EDD(封装驱动设计)方法和MailProxy实战案例,帮你掌握"从粗粒度到细粒度"的系统化模块划分方法,避免"想到哪,切到哪"的随意设计。
一、核心知识点:连续论证"是什么-为什么-怎么选"
知识点1:模块划分的4种思路——从"拍脑袋"到系统化方法
通俗理解:模块划分就像"切蛋糕"——你可以从上往下切(自顶向下),从下往上切(自底向上),或者随便切(拍脑袋)。但专业的架构师会用系统化的方法,确保每一刀都切在关键位置。
核心作用:理解模块划分的不同思路,是选择合适方法的前提。不同的思路适用于不同的场景,系统化的方法能够避免"想到哪,切到哪"的随意设计,确保模块划分的合理性和可维护性。
深入论证:为什么需要系统化的模块划分方法?这背后解决的是什么业务问题?
业务痛点1:随意划分导致模块耦合度高、难以维护
想象一个电商系统:如果按照"想到哪,切到哪"的方式划分模块,可能会出现什么问题?
- 问题1:模块职责不清:订单模块既处理订单逻辑,又处理支付逻辑,还处理库存逻辑。当需要修改支付逻辑时,需要修改订单模块,影响其他功能。
- 问题2:模块间耦合度高:用户模块直接调用订单模块的内部实现,订单模块直接调用库存模块的内部实现。当需要修改某个模块时,需要修改多个模块,风险高。
- 问题3:无法支持深度优先开发:如果模块划分只停留在粗粒度(如"业务层"、“数据层”),无法识别细粒度模块,就无法进行"深度优先"开发——无法先开发一个完整功能,再开发另一个功能,只能按层开发,导致无法快速交付价值。
解决方案:系统化的模块划分方法
模块划分的4种思路,从设计成果、设计思想、划分依据三个维度,提供了系统化的方法:
思路1:自顶向下——从系统整体到模块细节
(也能让领导知道你的想法,而不是拍脑袋,无依无据)
本质原理:自顶向下的思路是从系统整体出发,先确定系统的整体结构(如分层架构),再逐步细化到模块。这就像盖房子,先确定整体结构(几层楼、几个房间),再设计每个房间的细节。
为什么这样设计:
- 解决业务痛点"系统复杂度高":大型系统功能多、技术复杂,如果一开始就关注细节,容易迷失方向。自顶向下的思路先确定整体结构,再逐步细化,降低了系统复杂度。
- 支持分层架构设计:自顶向下的思路天然支持分层架构——先确定有哪些层(UI层、业务层、数据层),再在每层内划分模块。这解决了业务痛点"需要清晰的职责划分"。
- 支持功能模块划分:自顶向下的思路也支持功能模块划分——先确定有哪些功能模块(订单模块、用户模块),再在模块内划分细粒度模块。这解决了业务痛点"需要支持多人协作开发"。
决策逻辑:
- 用自顶向下:系统复杂度高,需要先确定整体结构;需要设计分层架构;需要设计功能模块。
- 不用自顶向下:系统非常简单,只有单一功能,不需要整体规划。
思路2:自底向上——从类到模块
本质原理:自底向上的思路是从具体的类出发,先识别系统中的类,然后将相关的类归纳为模块。这就像整理工具箱,先收集所有工具,再把相关的工具放在同一个工具箱里。
为什么这样设计:
- 解决业务痛点"系统难点部分的设计":对于系统难点部分(如复杂的业务逻辑),如果直接用自顶向下的思路,可能无法准确识别模块。自底向上的思路从具体的类出发,通过分析类的协作关系,识别模块,更适合处理复杂场景。
- 支持用例驱动设计:自底向上的思路天然支持用例驱动设计——先识别用例涉及的类,然后归纳为模块。这解决了业务痛点"需要处理复杂业务逻辑"。
决策逻辑:
- 用自底向上:系统难点部分的设计;需要处理复杂业务逻辑;需要用例驱动设计。
- 不用自底向上:系统整体设计;需要先确定整体结构。
思路3:水平切分(分层)——按技术关注点划分
本质原理:水平切分是按照技术关注点划分模块,将系统分为多个层次(如UI层、业务层、数据层),每层负责一种技术关注点。这就像餐厅的组织结构,服务员负责接待(UI层),厨师负责做菜(业务层),仓库管理员负责管理食材(数据层)。
为什么这样设计:
- 解决业务痛点"技术关注点分离":系统需要处理多种技术关注点(用户交互、业务逻辑、数据访问),如果混在一起,会导致代码混乱。水平切分将不同的技术关注点分离到不同层次,实现了关注点分离。
- 支持技术替换:如果层间接口设计合理(使用接口进行抽象ing),替换某一层的实现(如将MySQL换成PostgreSQL)不会影响其他层。这解决了业务痛点"外部系统接口可能变化"。
水平切分示意图:
决策逻辑:
- 用水平切分:系统需要处理多种技术关注点;需要支持技术替换;需要清晰的职责划分。
- 不用水平切分:系统是纯计算系统,不涉及外部交互。
思路4:垂直切分(功能模块)——按业务功能划分
本质原理:垂直切分是按照业务功能划分模块,将系统分为多个功能模块(如订单模块、用户模块),每个模块负责一个业务领域。这就像公司的部门划分,销售部门负责销售,财务部门负责财务。
为什么这样设计:
- 解决业务痛点"业务功能分离":系统需要处理多种业务功能(订单管理、用户管理),如果混在一起,会导致代码混乱。垂直切分将不同的业务功能分离到不同模块,实现了业务功能分离。
- 支持功能独立开发:不同的功能模块可以分配给不同的开发小组,实现并行开发。这解决了业务痛点"需要支持多人协作开发"。
垂直切分示意图:
决策逻辑:
- 用垂直切分:系统需要处理多种业务功能;需要支持功能独立开发;需要支持多人协作开发。
- 不用垂直切分:系统是单一功能系统,不需要功能分离。
思路5:拍脑袋——随意划分(不推荐)
本质原理:拍脑袋是随意划分模块,没有系统化的方法,想到哪,切到哪。这就像没有规划就盖房子,想到什么房间就建什么房间,容易导致结构混乱。
为什么这样设计:
- 不推荐的原因:拍脑袋的方式没有系统化的方法,容易导致模块划分不合理——模块职责不清、模块间耦合度高、无法支持深度优先开发。这无法解决任何业务痛点,反而会带来更多问题。
决策逻辑:
- 避免拍脑袋:所有系统都应该使用系统化的方法,避免随意划分。
知识点2:水平切分与垂直切分的综合应用——层、功能模块、细粒度模块的关系
通俗理解:水平切分和垂直切分不是对立的,而是互补的。就像切蛋糕,你可以先横着切几层(水平切分),再竖着切几块(垂直切分),最后在每块上切出小块(细粒度模块)。
核心作用:理解层、功能模块、细粒度模块的关系,是实现"分层+分区"综合应用的关键。只有同时考虑水平切分和垂直切分,才能识别细粒度模块,支持深度优先开发。
深入论证:为什么需要同时考虑水平切分和垂直切分?这背后解决的是什么业务问题?
业务痛点:止于粗粒度分层无法采用"深度优先"开发模式
想象一个电商系统:如果只进行粗粒度分层(如"业务层"、“数据层”),会出现什么问题?
- 问题1:无法识别细粒度模块:业务层包含订单模块、用户模块、支付模块等多个模块,但如果不进行细粒度划分,就无法识别这些模块,无法进行深度优先开发。
- 问题2:无法支持迭代开发:如果只按层开发(先开发所有UI层,再开发所有业务层),无法先开发一个完整功能(如订单功能),再开发另一个功能(如用户功能),导致无法快速交付价值。
- 问题3:模块协作链不清晰:如果无法识别细粒度模块,就无法识别模块之间的协作链(如订单UI模块→订单业务模块→订单数据模块),无法进行深度优先开发。
解决方案:分层+分区的综合应用
通过同时考虑水平切分(分层)和垂直切分(分区),可以实现细粒度模块划分,支持深度优先开发。
层、功能模块、细粒度模块的关系:
关系说明:
- 层(Layer):水平切分的结果,按技术关注点划分(如UI层、业务层、数据层)。
- 功能模块(子系统):垂直切分的结果,按业务功能划分(如订单模块、用户模块)。
- 细粒度模块:层和功能模块的交集,位于特定层的特定功能模块中的小模块(如订单UI模块、订单业务模块、订单数据模块)。
为什么这样设计:
- 解决业务痛点"无法支持深度优先开发":通过识别细粒度模块,可以识别模块之间的协作链(如订单UI模块→订单业务模块→订单数据模块),支持深度优先开发——先开发一个完整功能,再开发另一个功能。
- 支持迭代开发:基于细粒度模块的协作链,可以进行"深度优先"式开发——先开发订单功能的完整协作链,再开发用户功能的完整协作链,实现快速交付价值。
垂直切分与水平切分的综合应用示例:
为什么这样设计:
拓扑子系统采用领域模型模式:
- 业务逻辑复杂:图元的排列、连接、移动、覆盖、复制、删除等操作涉及不同的业务规则,需要复杂的对象协作关系。
- 领域模型模式的核心思想:将业务逻辑封装在领域对象(Domain Object)中,让对象自己负责业务规则的处理。这就像"让专业的人做专业的事"——图元对象自己知道如何排列、如何连接,而不是把所有逻辑都放在一个大的Service类中。
- 为什么适合复杂业务:当业务规则复杂、需要多个对象协作时,领域模型模式能够充分利用面向对象的优势(封装、继承、多态),让代码更清晰、更易维护。例如,图元的"移动"操作可能涉及碰撞检测、边界检查、连接关系维护等多个规则,如果都用SQL或过程式代码处理,会导致代码混乱、难以理解。
- 与事务脚本模式的区别:事务脚本模式是将业务逻辑写成过程式的脚本(如一个Service方法处理一个业务流程),适合简单业务;领域模型模式是将业务逻辑封装在对象中,适合复杂业务。
报表子系统采用事务脚本模式:
- 业务逻辑相对简单:通过SQL语句从数据库中提取数据,进行统计和查找计算,逻辑简单直接。
- 事务脚本模式的核心思想:将业务逻辑写成过程式的脚本,每个业务流程对应一个方法(如
generateReport()方法),方法内部通过SQL查询数据、进行计算、返回结果。 - 为什么适合简单业务:对于简单的CRUD操作和SQL查询,事务脚本模式更直接、更高效,不需要引入复杂的对象模型。如果强行使用领域模型模式,反而会增加复杂度,是"自找麻烦"。
决策逻辑:
- 同时用水平切分和垂直切分:系统需要处理多种技术关注点和多种业务功能;需要支持深度优先开发;需要支持迭代开发。
- 只用水平切分:系统是单一功能系统,不需要功能分离。
- 只用垂直切分:系统是单一技术关注点,不需要技术分离。
知识点3:EDD方法的4个步骤——从需求到细粒度模块的系统化方法
通俗理解:EDD(封装驱动设计)方法就像"盖房子的4步流程"——先研究需求(确定要盖什么样的房子),再分层(确定有几层楼),再划分模块(确定每个房间的功能),最后评审优化(检查房子是否合理)。
核心作用:EDD方法提供了系统化的模块划分流程,通过4个步骤(研究需求、分层、划分模块、评审优化),实现从粗粒度到细粒度的模块划分,避免"想到哪,切到哪"的随意设计。
深入论证:为什么需要EDD方法?这背后解决的是什么业务问题?
业务痛点:模块划分缺乏系统化方法,导致设计不合理
想象一个电商系统:如果按照"想到哪,切到哪"的方式划分模块,会出现什么问题?
- 问题1:设计不充分:如果架构设计止于粗粒度分层(如"业务层"、“数据层”),无法识别细粒度模块,无法支持深度优先开发,无法快速交付价值。
- 问题2:模块划分不合理:如果模块划分没有系统化的方法,容易导致模块职责不清、模块间耦合度高、无法支持模块复用。
- 问题3:无法支持迭代开发:如果无法识别细粒度模块,就无法进行"深度优先"式开发,无法先开发一个完整功能,再开发另一个功能,导致无法快速交付价值。
解决方案:EDD方法的4个步骤
EDD方法通过4个步骤,实现系统化的模块划分:
graph TB
Start([开始])
subgraph "步骤1:研究需求"
研究上下文图[研究上下文图]
研究功能树[研究功能树]
end
subgraph "步骤2:分层"
粗粒度分层[粗粒度分层]
end
subgraph "步骤3:划分模块"
细粒度划分模块[细粒度划分模块]
end
subgraph "步骤4:评审优化"
评审优化[用例驱动的模块划分结构<br/>评审、优化]
end
End([结束])
Start --> 研究上下文图
Start --> 研究功能树
研究上下文图 --> 粗粒度分层
研究功能树 --> 粗粒度分层
粗粒度分层 --> 细粒度划分模块
细粒度划分模块 --> 评审优化
评审优化 --> End
style 研究上下文图 fill:#e1f5ff
style 研究功能树 fill:#e1f5ff
style 粗粒度分层 fill:#fff4e1
style 细粒度划分模块 fill:#4caf50
style 评审优化 fill:#ffeb3b
步骤1:研究需求——为模块划分提供基础
本质原理:研究需求是模块划分的基础,通过研究上下文图和功能树,识别系统的外部实体和功能需求,为后续的分层和模块划分提供依据。
为什么这样设计:
- 解决业务痛点"设计不充分":如果模块划分没有需求基础,容易导致设计不充分——遗漏重要功能、模块划分不合理。研究需求可以确保模块划分有需求基础,避免设计不充分。
- 上下文图促进分层:上下文图描述了系统与外部世界的关系,直接决定了需要哪些封装层(UI层、SI层、DM层)。这解决了业务痛点"需要清晰的职责划分"。
- 功能树促进功能模块划分:功能树描述了系统的功能需求,直接决定了需要哪些功能模块(订单模块、用户模块)。这解决了业务痛点"需要支持多人协作开发"。
决策逻辑:
- 必须研究上下文图:设计分层架构时,必须研究上下文图,识别外部实体,设计对应的封装层。
- 必须研究功能树:设计功能模块时,必须研究功能树,识别功能需求,设计对应的功能模块。
步骤2:分层——确定系统的层次结构
本质原理:分层是确定系统的层次结构,通过粗粒度分层(如UI层、业务层、数据层),实现技术关注点分离。
为什么这样设计:
- 解决业务痛点"技术关注点分离":系统需要处理多种技术关注点(用户交互、业务逻辑、数据访问),如果混在一起,会导致代码混乱。分层将不同的技术关注点分离到不同层次,实现了关注点分离。
- 为细粒度模块划分提供基础:一旦确定了分层架构,层内的模块划分就变得相对容易,因为每个层的职责已经明确。这解决了业务痛点"需要支持多人协作开发"。
决策逻辑:
- 必须分层:所有系统都应该进行分层,实现技术关注点分离。
- 分层参考:可以参考第13章的分层架构设计方法。
步骤3:划分模块——细粒度模块划分
本质原理:划分模块是在分层的基础上,进行细粒度模块划分,通过分层细化、分区、通用模块分离、通用机制框架化等方法,实现细粒度模块划分。
为什么这样设计:
- 解决业务痛点"无法支持深度优先开发":如果架构设计止于粗粒度分层,无法识别细粒度模块,就无法进行深度优先开发。细粒度模块划分可以识别模块之间的协作链,支持深度优先开发。
- 支持迭代开发:基于细粒度模块的协作链,可以进行"深度优先"式开发——先开发一个完整功能,再开发另一个功能,实现快速交付价值。
细粒度模块划分技巧:
技巧1:分层细化(Layer内细分Sub-layer)
本质原理:在粗粒度层内,进一步细化职责,形成子层(Sub-layer)。这就像餐厅的组织结构,服务员部门内还有前台服务员和包间服务员。
为什么这样设计:
- 解决业务痛点"粗粒度分层不够":经典的3层或4层架构可能对投标足够,但绝对不足以指导后续的详细设计和并行开发。分层细化可以进一步细化职责,支持详细设计和并行开发。
分层细化示例:
决策逻辑:
- 需要分层细化:粗粒度分层不足以指导详细设计和并行开发;需要进一步细化职责。
- 不需要分层细化:系统非常简单,粗粒度分层已经足够。
技巧2:分区(Partitioning)
本质原理:将"层"内支持不同功能组的职责分别"封装"到"细粒度模块"中,从而使每个"粗粒度功能模块"由一组位于不同"层"的"细粒度模块"组成。
为什么这样设计:
- 解决业务痛点"无法支持深度优先开发":通过分区,可以识别每个功能模块在不同层的细粒度模块,形成协作链(如订单UI模块→订单业务模块→订单数据模块),支持深度优先开发。
决策逻辑:
- 需要分区:系统需要支持深度优先开发;需要支持迭代开发。
- 不需要分区:系统是单一功能系统,不需要功能分离。
技巧3:通用模块的分离
本质原理:将通用模块(如错误报告、日志记录、安全验证)独立出来,供多个功能模块使用,但不依赖特定的功能模块。
为什么这样设计:
- 解决业务痛点"模块复用":通用模块的分离有利于提高重用性、减少重复代码。如果通用模块放在某个功能模块中,会导致其他模块依赖这个模块,增加耦合度。
通用模块的扇入特性:
为什么这样设计:
- 高层模块应有较高的扇出:高层模块调用多个低层模块,实现复杂功能。
- 低层模块特别是底层模块应有较高的扇入:通用模块被多个上级模块共享,实现代码复用。
- 扇入越大,表示该模块被更多的上级模块共享:通用模块的扇入数大,说明它被多个模块使用,实现了代码复用。
决策逻辑:
- 需要分离通用模块:有公共服务被多个功能模块使用;需要提高代码复用性。
- 不需要分离通用模块:没有公共服务;系统非常简单。
技巧4:通用机制的框架化
本质原理:将通用机制(如数据库管理机制、事件通知机制、事务服务机制)框架化,通过回调机制实现扩展。
为什么这样设计:
- 解决业务痛点"架构稳定性":通用机制的框架化是架构稳定性的不可或缺部分。框架就像大楼的供暖、供电、排水设施,解决整个结构的共性问题。
- 解决"重用概率"与"重用价值"的矛盾:软件单元的粒度越大,重用带来的价值量越大,但重用概率越低;粒度越小,重用概率越高,但重用带来的价值量越小。框架的智慧在于解决这个矛盾——将容易变化的部分封装为"扩展点",通过回调机制纳入框架控制,在重用价值量和定制成本之间取得平衡。
决策逻辑:
- 需要框架化通用机制:有通用机制被多个模块使用;需要提高架构稳定性。
- 不需要框架化通用机制:没有通用机制;系统非常简单。
步骤4:评审优化——用例驱动的模块划分结构评审、优化
本质原理:评审优化是通过用例驱动的方法,评审和优化模块划分结构,确保模块划分的合理性和可维护性。
为什么这样设计:
- 解决业务痛点"模块划分不合理":如果模块划分没有评审,容易导致模块划分不合理——模块职责不清、模块间耦合度高。评审优化可以及早发现设计问题,避免后续返工。
- 用例驱动评审:系统难点部分的设计,可多使用用例驱动方法。场景和用例,也是"驱动"设计评审的核心思维。
决策逻辑:
- 必须评审优化:所有模块划分都应该进行评审优化,确保设计合理性。
- 用例驱动评审:系统难点部分的设计,应该使用用例驱动方法进行评审。
知识点4:细粒度模块划分的4个技能项——从粗粒度到细粒度的关键技巧
通俗理解:细粒度模块划分就像"把大房间分成小房间"——分层细化是把大房间分成几个小房间,分区是把房间按功能划分,通用模块分离是把公共设施单独管理,通用机制框架化是把基础设施标准化。
核心作用:细粒度模块划分的4个技能项(分层细化、分区、通用模块分离、通用机制框架化)是实现从粗粒度到细粒度模块划分的关键技巧,支持深度优先开发和迭代开发。
深入论证:为什么需要细粒度模块划分?这背后解决的是什么业务问题?
业务痛点:止于粗粒度分层无法采用"深度优先"开发模式
想象一个电商系统:如果架构设计止于粗粒度分层(如"业务层"、“数据层”),会出现什么问题?
- 问题1:无法识别细粒度模块:业务层包含订单模块、用户模块、支付模块等多个模块,但如果不进行细粒度划分,就无法识别这些模块,无法进行深度优先开发。
- 问题2:无法支持迭代开发:如果只按层开发(先开发所有UI层,再开发所有业务层),无法先开发一个完整功能(如订单功能),再开发另一个功能(如用户功能),导致无法快速交付价值。
- 问题3:模块协作链不清晰:如果无法识别细粒度模块,就无法识别模块之间的协作链(如订单UI模块→订单业务模块→订单数据模块),无法进行深度优先开发。
解决方案:细粒度模块划分的4个技能项
通过分层细化、分区、通用模块分离、通用机制框架化,可以实现细粒度模块划分,支持深度优先开发。
粗粒度分层 vs 细粒度模块划分对比:
graph TB
subgraph "粗粒度分层(无法深度优先开发)"
粗粒度层1[层1]
粗粒度层2[层2]
粗粒度层3[层3]
粗粒度层4[层4]
end
subgraph "细粒度模块划分(支持深度优先开发)"
细粒度层1[层1<br/>模块1 模块2 模块3]
细粒度层2[层2<br/>模块1 模块2 模块3]
细粒度层3[层3<br/>模块1 模块2 模块3]
细粒度层4[层4<br/>模块1 模块2 模块3]
end
粗粒度层1 --> 粗粒度层2
粗粒度层2 --> 粗粒度层3
粗粒度层3 --> 粗粒度层4
细粒度层1 --> 细粒度层2
细粒度层2 --> 细粒度层3
细粒度层3 --> 细粒度层4
协作链[协作链<br/>模块1→模块2→模块3→模块1]
协作链 -.支持.-> 细粒度层1
协作链 -.支持.-> 细粒度层2
协作链 -.支持.-> 细粒度层3
协作链 -.支持.-> 细粒度层4
style 粗粒度层1 fill:#ffcdd2
style 粗粒度层2 fill:#ffcdd2
style 粗粒度层3 fill:#ffcdd2
style 粗粒度层4 fill:#ffcdd2
style 细粒度层1 fill:#c8e6c9
style 细粒度层2 fill:#c8e6c9
style 细粒度层3 fill:#c8e6c9
style 细粒度层4 fill:#c8e6c9
style 协作链 fill:#ffeb3b
为什么这样设计:
- 解决业务痛点"无法支持深度优先开发":通过细粒度模块划分,可以识别(todo:调用链路)模块之间的协作链(如订单UI模块→订单业务模块→订单数据模块),支持深度优先开发——先开发一个完整功能,再开发另一个功能。
- 支持迭代开发:基于细粒度模块的协作链,可以进行"深度优先"式开发——(todo:没明白)先开发订单功能的完整协作链,再开发用户功能的完整协作链,实现快速交付价值。
理想的模块划分结构:
理想结构的特征:
- 设计充分:最终是由一组细粒度"模块"组成的。
- 层次结构清晰:但不限于3层或4层,而是进行了分层细化。
- 功能模块概念清晰:每个"功能模块"由一组位于不同"层"的细粒度模块组成。
- 通用模块被单独划分出来:通用模块独立管理,供多个功能模块使用。
决策逻辑:
- 需要细粒度模块划分:系统复杂度高,需要支持深度优先开发;需要支持迭代开发。
- 不需要细粒度模块划分:系统非常简单,粗粒度分层已经足够。
二、实战场景串联
场景1:MailProxy系统的模块划分设计——从业务需求到架构落地
业务背景:某公司需要开发MailProxy系统,用于处理用户邮件请求,与Mail Server交互发送和接收邮件,与客户系统集成提供邮件服务,存储邮件数据到数据库。
业务痛点识别:
痛点1:需要与多种外部实体交互,技术细节复杂
- 需要处理用户邮件请求(Web界面)
- 需要与Mail Server交互(发送邮件、接收邮件)
- 需要与客户系统集成(提供邮件服务接口)
- 需要存储邮件数据到数据库(Oracle、SQL Server等)
- 如果业务逻辑直接处理这些交互,会导致业务逻辑与技术细节耦合,难以维护。
痛点2:外部系统接口可能变化,需要支持灵活扩展
- Mail Server可能更换(从Exchange换成Gmail),接口协议可能不同
- 客户系统可能升级,接口可能变化
- 数据库可能迁移(从Oracle换成SQL Server)
- 如果业务逻辑直接处理这些变化,会导致大量修改,风险高。
痛点3:需要支持深度优先开发,快速交付价值
- 系统需要支持迭代开发,先开发一个完整功能(如邮件发送),再开发另一个功能(如邮件接收)
- 如果架构设计止于粗粒度分层,无法识别细粒度模块,就无法进行深度优先开发,无法快速交付价值。
技术挑战:如何设计合理的模块划分?需要哪些层?每层有哪些细粒度模块?如何确保架构能够支持系统的长期演进?
解决方案:用EDD方法解决业务痛点
第一步:研究需求——识别外部实体和功能需求
核心思路:EDD方法的第一步是研究需求,通过研究上下文图和功能树,识别系统的外部实体和功能需求,为后续的分层和模块划分提供依据。
识别外部实体(通过上下文图):
外部实体1:外部用户——对应业务痛点"需要处理用户邮件请求"
- 识别结果:用户(通过Web界面发送邮件)
- 业务价值:系统需要提供Web界面,用户通过界面发送邮件,这是系统的核心用户交互方式。如果不识别,会导致无法设计UI层,业务逻辑需要直接处理Web请求,增加复杂度。
外部实体2:外部系统——对应业务痛点"需要与Mail Server和客户系统交互"
- 识别结果:
- Mail Server(邮件服务器,通过邮件协议交互)
- 客户系统(需要集成的外部系统,通过HTTP接口集成)
- 业务价值:系统需要与Mail Server交互(发送邮件、接收邮件),需要与客户系统集成(提供邮件服务)。如果不识别,会导致无法设计SI层,业务逻辑需要直接处理邮件协议和HTTP接口,增加复杂度。
外部实体3:持久化存储——对应业务痛点"需要存储邮件数据"
- 识别结果:Oracle、SQL Server等数据库系统(存储邮件数据)
- 业务价值:系统需要存储邮件数据,需要使用数据库。如果不识别,会导致无法设计DM层,业务逻辑需要直接处理数据库操作,增加复杂度。
识别功能需求(通过功能树):
功能需求1:邮件发送功能
- 用户通过Web界面发送邮件
- 系统与Mail Server交互发送邮件
- 系统存储邮件发送记录到数据库
功能需求2:邮件接收功能
- 系统定时从Mail Server接收邮件
- 系统存储邮件接收记录到数据库
功能需求3:邮件管理功能
- 管理员设置邮件地址
- 管理员设置邮件规则
- 系统存储设置信息到数据库
为什么这样识别:
- 研究上下文图:上下文图描述了系统与外部世界的关系,直接决定了需要哪些封装层(UI层、SI层、DM层)。这解决了业务痛点"需要清晰的职责划分"。
- 研究功能树:功能树描述了系统的功能需求,直接决定了需要哪些功能模块(邮件发送模块、邮件接收模块、邮件管理模块)。这解决了业务痛点"需要支持多人协作开发"。
第二步:分层——确定系统的层次结构
核心思路:EDD方法的第二步是分层,通过粗粒度分层(如UI层、业务层、数据层),实现技术关注点分离。
设计分层架构:
为什么这样设计:
- 解决业务痛点"技术关注点分离":系统需要处理多种技术关注点(用户交互、外部系统交互、业务逻辑、数据访问),如果混在一起,会导致代码混乱。分层将不同的技术关注点分离到不同层次,实现了关注点分离。
- 为细粒度模块划分提供基础:一旦确定了分层架构,层内的模块划分就变得相对容易,因为每个层的职责已经明确。这解决了业务痛点"需要支持多人协作开发"。
第三步:划分模块——细粒度模块划分
核心思路:EDD方法的第三步是划分模块,在分层的基础上,进行细粒度模块划分,通过分层细化、分区、通用模块分离等方法,实现细粒度模块划分。
细粒度模块划分结果:
为什么这样设计:
分层细化(Layer内细分Sub-layer):
- 用户交互层:封装用户交互(设置地址、设置规则等)
- 系统交互层:封装外部系统交互(代理模块、Mail Server交互模块)
- 业务逻辑层:实现业务逻辑(调度、设置地址、设置规则、发送、反馈等)
- 数据访问层:封装数据访问(设置信息读写、邮件信息读写、发送结果信息读写等)
分区(Partitioning):
- 邮件发送功能模块:由用户交互层的"设置地址"、业务逻辑层的"发送"、数据访问层的"邮件信息读写"等细粒度模块组成
- 邮件接收功能模块:由系统交互层的"Mail Server交互模块"、业务逻辑层的"反馈"、数据访问层的"发送结果信息读写"等细粒度模块组成
- 邮件管理功能模块:由用户交互层的"设置地址"、“设置规则”、业务逻辑层的"设置地址"、“设置规则”、数据访问层的"设置信息读写"等细粒度模块组成
通用模块分离:
- 通用框架:业务逻辑层中的"通用框架",供多个功能模块使用,但不依赖特定的功能模块
解决业务痛点"需要支持深度优先开发":
- 识别协作链:通过细粒度模块划分,可以识别模块之间的协作链(如设置地址UI模块→设置地址业务模块→设置信息读写模块),支持深度优先开发——先开发一个完整功能,再开发另一个功能。
- 支持迭代开发:基于细粒度模块的协作链,可以进行"深度优先"式开发——先开发邮件发送功能的完整协作链,再开发邮件接收功能的完整协作链,实现快速交付价值。
第四步:评审优化——用例驱动的模块划分结构评审、优化
核心思路:EDD方法的第四步是评审优化,通过用例驱动的方法,评审和优化模块划分结构,确保模块划分的合理性和可维护性。
评审要点:
评审1:模块职责清晰性
- 验证标准:每个细粒度模块职责单一,没有职责重叠。
- 验证结果:每个细粒度模块职责清晰,没有职责重叠。
- 业务价值:如果模块职责不清晰,会导致代码混乱,难以维护。职责清晰使得每个模块独立,便于理解和修改。
评审2:模块协作链完整性
- 验证标准:每个功能模块都有完整的协作链(UI模块→业务模块→数据模块)。
- 验证结果:邮件发送功能模块、邮件接收功能模块、邮件管理功能模块都有完整的协作链。
- 业务价值:如果协作链不完整,就无法进行深度优先开发,无法快速交付价值。完整的协作链支持深度优先开发,实现快速交付价值。
评审3:通用模块复用性
- 验证标准:通用模块被多个功能模块使用,但不依赖特定的功能模块。
- 验证结果:通用框架被多个功能模块使用,但不依赖特定的功能模块。
- 业务价值:如果通用模块依赖特定的功能模块,会增加耦合度,降低复用性。通用模块独立管理,提高复用性,降低耦合度。
为什么这样评审:
- 解决业务痛点"模块划分不合理":如果模块划分没有评审,容易导致模块划分不合理——模块职责不清、模块间耦合度高。评审优化可以及早发现设计问题,避免后续返工。
- 用例驱动评审:系统难点部分的设计,可多使用用例驱动方法。场景和用例,也是"驱动"设计评审的核心思维。
设计优点和缺点分析
优点1:更严谨、更专业,可操作性强
- 更严谨:EDD方法提供了系统化的模块划分流程,避免了"想到哪,切到哪"的随意设计。
- 更专业:综合了多种手段(分层、分区、通用模块分离、通用机制框架化),促进了细粒度模块划分。
- 可操作性强:4个步骤清晰明确,每个步骤都有具体的操作要点,便于执行。
优点2:综合了多种手段,促进了细粒度模块划分
- 分层:通过粗粒度分层,实现技术关注点分离。
- 分区:通过分区,实现功能模块划分。
- 通用模块分离:通过通用模块分离,提高代码复用性。
- 通用机制框架化:通过通用机制框架化,提高架构稳定性。
优点3:加入了评审优化环节
- 评审优化:通过用例驱动的方法,评审和优化模块划分结构,确保模块划分的合理性和可维护性。
缺点1:因为是综合方法,所以掌握起来还是稍有门槛的
- 学习成本:EDD方法综合了多种手段,需要理解分层、分区、通用模块分离、通用机制框架化等多个概念,学习成本较高。
- 实践经验:有没有经验,决定你能不能成为架构师。只有通过实践,才能掌握EDD方法的精髓。
长期适配策略:
策略1:在细粒度模块基础上进行迭代开发
- 当前状态:通过EDD方法,已经识别了细粒度模块,形成了模块协作链。
- 长期价值:基于细粒度模块的协作链,可以进行"深度优先"式开发——先开发邮件发送功能的完整协作链,再开发邮件接收功能的完整协作链,实现快速交付价值。
- 实施建议:每个迭代周期,选择一个功能模块,开发其完整的协作链,实现快速交付价值。
策略2:持续优化模块划分结构
- 当前状态:通过评审优化,已经确保了模块划分的合理性。
- 长期价值:随着系统演进,可能需要调整模块划分结构(如增加新功能模块、合并相似模块),需要持续优化。
- 实施建议:定期评审模块划分结构,根据系统演进情况,调整模块划分,确保架构的合理性。
三、总结收尾
通用应用逻辑公式
模块划分的通用逻辑可以总结为:“研究需求→分层→划分模块→评审优化”。
公式拆解:
- 研究需求:通过研究上下文图和功能树,识别系统的外部实体和功能需求,为后续的分层和模块划分提供依据。这就像盖房子,先确定要盖什么样的房子(需求),再设计房子结构(架构)。
- 分层:通过粗粒度分层(如UI层、业务层、数据层),实现技术关注点分离。这解决了业务痛点"需要清晰的职责划分"。
- 划分模块:在分层的基础上,进行细粒度模块划分,通过分层细化、分区、通用模块分离、通用机制框架化等方法,实现细粒度模块划分。这解决了业务痛点"需要支持深度优先开发"。
- 评审优化:通过用例驱动的方法,评审和优化模块划分结构,确保模块划分的合理性和可维护性。这解决了业务痛点"模块划分不合理"。
通俗比喻:就像"盖房子的4步流程"——先研究需求(确定要盖什么样的房子),再分层(确定有几层楼),再划分模块(确定每个房间的功能),最后评审优化(检查房子是否合理)。
可落地的交付物
模块划分设计检查清单:
-
研究需求:
- 研究了上下文图,识别了外部实体(外部用户、持久化存储、外部系统/硬件、时限触发)
- 研究了功能树,识别了功能需求(功能类别、功能组、功能项)
- 确保需求完整准确,为模块划分提供基础
-
分层:
- 进行了粗粒度分层(UI层、业务层、数据层等)
- 实现了技术关注点分离
- 为细粒度模块划分提供基础
-
划分模块:
- 进行了分层细化(Layer内细分Sub-layer)
- 进行了分区(将功能组映射到功能模块)
- 分离了通用模块(所有功能模块都需要的基础服务)
- 框架化了通用机制(数据库管理机制、事件通知机制等)
-
评审优化:
- 评审了模块职责清晰性(每个模块职责单一,没有职责重叠)
- 评审了模块协作链完整性(每个功能模块都有完整的协作链)
- 评审了通用模块复用性(通用模块被多个功能模块使用,但不依赖特定的功能模块)
- 使用了用例驱动方法进行评审优化
最简操作模板:
模块划分四步走:
1. 研究需求:研究上下文图(识别外部实体)和研究功能树(识别功能需求)
2. 分层:进行粗粒度分层,实现技术关注点分离
3. 划分模块:进行细粒度模块划分(分层细化、分区、通用模块分离、通用机制框架化)
4. 评审优化:用例驱动的模块划分结构评审、优化
回答核心问题
问题1:为什么需要系统化的模块划分方法?
核心原因:
- 避免随意设计:如果按照"想到哪,切到哪"的方式划分模块,容易导致模块职责不清、模块间耦合度高、无法支持深度优先开发。
- 支持深度优先开发:只有通过系统化的方法,识别细粒度模块,形成模块协作链,才能支持深度优先开发——先开发一个完整功能,再开发另一个功能。
- 提高架构质量:系统化的方法能够确保模块划分的合理性和可维护性,提高架构质量。
适用场景:
- 需要系统化方法:所有系统都应该使用系统化的方法,避免随意划分。
- EDD方法适用:系统复杂度高,需要支持深度优先开发;需要支持迭代开发。
问题2:如何实现从粗粒度到细粒度的模块划分?
实现方法:
- 分层细化:在粗粒度层内,进一步细化职责,形成子层(Sub-layer)。
- 分区:将"层"内支持不同功能组的职责分别"封装"到"细粒度模块"中,从而使每个"粗粒度功能模块"由一组位于不同"层"的"细粒度模块"组成。
- 通用模块分离:将通用模块(如错误报告、日志记录、安全验证)独立出来,供多个功能模块使用,但不依赖特定的功能模块。
- 通用机制框架化:将通用机制(如数据库管理机制、事件通知机制、事务服务机制)框架化,通过回调机制实现扩展。
实现原则:
- 同时考虑水平切分和垂直切分:系统需要处理多种技术关注点和多种业务功能,需要同时考虑水平切分(分层)和垂直切分(分区)。
- 识别细粒度模块:只有识别了细粒度模块,才能形成模块协作链,支持深度优先开发。
- 支持迭代开发:基于细粒度模块的协作链,可以进行"深度优先"式开发,实现快速交付价值。
记住:模块划分是架构师的"看家本领"。通过EDD方法,从需求出发,通过系统化的4个步骤(研究需求、分层、划分模块、评审优化),实现从粗粒度到细粒度的模块划分,避免"想到哪,切到哪"的随意设计,支持深度优先开发和迭代开发,让大型系统变得可管理。正如温昱所说:“有没有方法,决定一个架构师能走多远。”
参考:
《软件架构设计》温昱
1444

被折叠的 条评论
为什么被折叠?



