【软件架构设计方法论(12)】模块划分的4步骤方法:EDD方法与实践

模块划分是架构师的"看家本领"——通过系统化的方法将复杂系统分解为可管理的模块,实现关注点分离、模块复用和深度优先开发。正如Daniel D. Gajski所说:"系统设计中一个最重要的和最具有挑战性的任务就是…将系统所要执行的功能分到各个组件上。"本文通过EDD(封装驱动设计)方法MailProxy实战案例,帮你掌握"从粗粒度到细粒度"的系统化模块划分方法,避免"想到哪,切到哪"的随意设计。

 

一、核心知识点:连续论证"是什么-为什么-怎么选"

知识点1:模块划分的4种思路——从"拍脑袋"到系统化方法

通俗理解:模块划分就像"切蛋糕"——你可以从上往下切(自顶向下),从下往上切(自底向上),或者随便切(拍脑袋)。但专业的架构师会用系统化的方法,确保每一刀都切在关键位置。

核心作用:理解模块划分的不同思路,是选择合适方法的前提。不同的思路适用于不同的场景,系统化的方法能够避免"想到哪,切到哪"的随意设计,确保模块划分的合理性和可维护性。

深入论证:为什么需要系统化的模块划分方法?这背后解决的是什么业务问题?

业务痛点1:随意划分导致模块耦合度高、难以维护

想象一个电商系统:如果按照"想到哪,切到哪"的方式划分模块,可能会出现什么问题?

  • 问题1:模块职责不清:订单模块既处理订单逻辑,又处理支付逻辑,还处理库存逻辑。当需要修改支付逻辑时,需要修改订单模块,影响其他功能。
  • 问题2:模块间耦合度高:用户模块直接调用订单模块的内部实现,订单模块直接调用库存模块的内部实现。当需要修改某个模块时,需要修改多个模块,风险高。
  • 问题3:无法支持深度优先开发:如果模块划分只停留在粗粒度(如"业务层"、“数据层”),无法识别细粒度模块,就无法进行"深度优先"开发——无法先开发一个完整功能,再开发另一个功能,只能按层开发,导致无法快速交付价值。

解决方案:系统化的模块划分方法

模块划分的4种思路,从设计成果、设计思想、划分依据三个维度,提供了系统化的方法:

划分依据
设计思想
设计成果
水平切分
分层
垂直切分
功能模块
将类分组
自底向上
自顶向下
将系统切分为...
自底向上
先识别类,后...
拍脑袋
模块划分结构

思路1:自顶向下——从系统整体到模块细节
(也能让领导知道你的想法,而不是拍脑袋,无依无据)

本质原理:自顶向下的思路是从系统整体出发,先确定系统的整体结构(如分层架构),再逐步细化到模块。这就像盖房子,先确定整体结构(几层楼、几个房间),再设计每个房间的细节。

为什么这样设计

  • 解决业务痛点"系统复杂度高":大型系统功能多、技术复杂,如果一开始就关注细节,容易迷失方向。自顶向下的思路先确定整体结构,再逐步细化,降低了系统复杂度。
  • 支持分层架构设计:自顶向下的思路天然支持分层架构——先确定有哪些层(UI层、业务层、数据层),再在每层内划分模块。这解决了业务痛点"需要清晰的职责划分"。
  • 支持功能模块划分:自顶向下的思路也支持功能模块划分——先确定有哪些功能模块(订单模块、用户模块),再在模块内划分细粒度模块。这解决了业务痛点"需要支持多人协作开发"。

决策逻辑

  • 用自顶向下:系统复杂度高,需要先确定整体结构;需要设计分层架构;需要设计功能模块。
  • 不用自顶向下:系统非常简单,只有单一功能,不需要整体规划。

 

思路2:自底向上——从类到模块

本质原理:自底向上的思路是从具体的类出发,先识别系统中的类,然后将相关的类归纳为模块。这就像整理工具箱,先收集所有工具,再把相关的工具放在同一个工具箱里。

为什么这样设计

  • 解决业务痛点"系统难点部分的设计":对于系统难点部分(如复杂的业务逻辑),如果直接用自顶向下的思路,可能无法准确识别模块。自底向上的思路从具体的类出发,通过分析类的协作关系,识别模块,更适合处理复杂场景
  • 支持用例驱动设计:自底向上的思路天然支持用例驱动设计——先识别用例涉及的类,然后归纳为模块。这解决了业务痛点"需要处理复杂业务逻辑"。

决策逻辑

  • 用自底向上:系统难点部分的设计;需要处理复杂业务逻辑;需要用例驱动设计。
  • 不用自底向上:系统整体设计;需要先确定整体结构。

 

思路3:水平切分(分层)——按技术关注点划分

本质原理:水平切分是按照技术关注点划分模块,将系统分为多个层次(如UI层、业务层、数据层),每层负责一种技术关注点。这就像餐厅的组织结构,服务员负责接待(UI层),厨师负责做菜(业务层),仓库管理员负责管理食材(数据层)。

为什么这样设计

  • 解决业务痛点"技术关注点分离":系统需要处理多种技术关注点(用户交互、业务逻辑、数据访问),如果混在一起,会导致代码混乱。水平切分将不同的技术关注点分离到不同层次,实现了关注点分离。
  • 支持技术替换:如果层间接口设计合理(使用接口进行抽象ing),替换某一层的实现(如将MySQL换成PostgreSQL)不会影响其他层。这解决了业务痛点"外部系统接口可能变化"。

水平切分示意图

水平切分(分层)
UI交互层
问题领域层
数据管理层
系统交互层

决策逻辑

  • 用水平切分:系统需要处理多种技术关注点;需要支持技术替换;需要清晰的职责划分。
  • 不用水平切分:系统是纯计算系统,不涉及外部交互。

 

思路4:垂直切分(功能模块)——按业务功能划分

本质原理:垂直切分是按照业务功能划分模块,将系统分为多个功能模块(如订单模块、用户模块),每个模块负责一个业务领域。这就像公司的部门划分,销售部门负责销售,财务部门负责财务。

为什么这样设计

  • 解决业务痛点"业务功能分离":系统需要处理多种业务功能(订单管理、用户管理),如果混在一起,会导致代码混乱。垂直切分将不同的业务功能分离到不同模块,实现了业务功能分离。
  • 支持功能独立开发:不同的功能模块可以分配给不同的开发小组,实现并行开发。这解决了业务痛点"需要支持多人协作开发"。

垂直切分示意图

垂直切分(功能模块)
功能模块1
功能模块2
功能模块3
功能模块4
通用模块

决策逻辑

  • 用垂直切分:系统需要处理多种业务功能;需要支持功能独立开发;需要支持多人协作开发。
  • 不用垂直切分:系统是单一功能系统,不需要功能分离。

 

思路5:拍脑袋——随意划分(不推荐)

本质原理:拍脑袋是随意划分模块,没有系统化的方法,想到哪,切到哪。这就像没有规划就盖房子,想到什么房间就建什么房间,容易导致结构混乱。

为什么这样设计

  • 不推荐的原因:拍脑袋的方式没有系统化的方法,容易导致模块划分不合理——模块职责不清、模块间耦合度高、无法支持深度优先开发。这无法解决任何业务痛点,反而会带来更多问题。

决策逻辑

  • 避免拍脑袋:所有系统都应该使用系统化的方法,避免随意划分。

 

知识点2:水平切分与垂直切分的综合应用——层、功能模块、细粒度模块的关系

通俗理解:水平切分和垂直切分不是对立的,而是互补的。就像切蛋糕,你可以先横着切几层(水平切分),再竖着切几块(垂直切分),最后在每块上切出小块(细粒度模块)。

核心作用:理解层、功能模块、细粒度模块的关系,是实现"分层+分区"综合应用的关键。只有同时考虑水平切分和垂直切分,才能识别细粒度模块,支持深度优先开发。

深入论证:为什么需要同时考虑水平切分和垂直切分?这背后解决的是什么业务问题?

业务痛点:止于粗粒度分层无法采用"深度优先"开发模式

想象一个电商系统:如果只进行粗粒度分层(如"业务层"、“数据层”),会出现什么问题?

  • 问题1:无法识别细粒度模块:业务层包含订单模块、用户模块、支付模块等多个模块,但如果不进行细粒度划分,就无法识别这些模块,无法进行深度优先开发。
  • 问题2:无法支持迭代开发:如果只按层开发(先开发所有UI层,再开发所有业务层),无法先开发一个完整功能(如订单功能),再开发另一个功能(如用户功能),导致无法快速交付价值。
  • 问题3:模块协作链不清晰:如果无法识别细粒度模块,就无法识别模块之间的协作链(如订单UI模块→订单业务模块→订单数据模块),无法进行深度优先开发。

解决方案:分层+分区的综合应用

通过同时考虑水平切分(分层)和垂直切分(分区),可以实现细粒度模块划分,支持深度优先开发。

层、功能模块、细粒度模块的关系

细粒度模块
功能模块(子系统)
层(Layer)
重叠
重叠
重叠
模块
细粒度
功能模块
子系统

关系说明

  • 层(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层架构可能对投标足够,但绝对不足以指导后续的详细设计和并行开发。分层细化可以进一步细化职责,支持详细设计和并行开发。

分层细化示例

系统交互层
数据管理层
业务层
UI层
Integration Services/API
File System Interface
RDBMS Interface
Services/API
Domain Objects
Presentation
Session

决策逻辑

  • 需要分层细化:粗粒度分层不足以指导详细设计和并行开发;需要进一步细化职责。
  • 不需要分层细化:系统非常简单,粗粒度分层已经足够。

技巧2:分区(Partitioning)

本质原理:将"层"内支持不同功能组的职责分别"封装"到"细粒度模块"中,从而使每个"粗粒度功能模块"由一组位于不同"层"的"细粒度模块"组成。

为什么这样设计

  • 解决业务痛点"无法支持深度优先开发":通过分区,可以识别每个功能模块在不同层的细粒度模块,形成协作链(如订单UI模块→订单业务模块→订单数据模块),支持深度优先开发。

决策逻辑

  • 需要分区:系统需要支持深度优先开发;需要支持迭代开发。
  • 不需要分区:系统是单一功能系统,不需要功能分离。

技巧3:通用模块的分离

本质原理:将通用模块(如错误报告、日志记录、安全验证)独立出来,供多个功能模块使用,但不依赖特定的功能模块。

为什么这样设计

  • 解决业务痛点"模块复用":通用模块的分离有利于提高重用性、减少重复代码。如果通用模块放在某个功能模块中,会导致其他模块依赖这个模块,增加耦合度。

通用模块的扇入特性

底层模块
中层模块
高层模块
通用模块
扇入数大
模块4
模块5
模块1
模块2
模块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:没明白)先开发订单功能的完整协作链,再开发用户功能的完整协作链,实现快速交付价值。

理想的模块划分结构

理想的模块划分结构
模块组1
模块1 模块2 模块3
模块组2
模块1 模块2
模块3 模块4
模块组3
模块1 模块2
模块3
模块组4
模块1 模块2
模块3
模块组5
模块1 模块2
模块3

理想结构的特征

  • 设计充分:最终是由一组细粒度"模块"组成的。
  • 层次结构清晰:但不限于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层、业务层、数据层),实现技术关注点分离。

设计分层架构

外部实体
MailProxy系统分层架构
用户
客户系统
Mail Server
Oracle SQL Server等
数据库系统
展现层
UI层
集成层
SI层
业务层
PD层
数据访问层
DM层

为什么这样设计

  • 解决业务痛点"技术关注点分离":系统需要处理多种技术关注点(用户交互、外部系统交互、业务逻辑、数据访问),如果混在一起,会导致代码混乱。分层将不同的技术关注点分离到不同层次,实现了关注点分离。
  • 为细粒度模块划分提供基础:一旦确定了分层架构,层内的模块划分就变得相对容易,因为每个层的职责已经明确。这解决了业务痛点"需要支持多人协作开发"。
第三步:划分模块——细粒度模块划分

核心思路:EDD方法的第三步是划分模块,在分层的基础上,进行细粒度模块划分,通过分层细化、分区、通用模块分离等方法,实现细粒度模块划分。

细粒度模块划分结果

数据库
数据访问层
业务逻辑层
系统交互层
用户交互层
Oracle SQL Server等
数据库系统
设置信息读写
邮件信息读写
发送结果信息读写
......
调度
设置地址
设置规则
发送
反馈
通用框架
......
代理模块
客户系统
Mail Server
交互模块
设置地址
设置规则
......

为什么这样设计

分层细化(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:持续优化模块划分结构

  • 当前状态:通过评审优化,已经确保了模块划分的合理性。
  • 长期价值:随着系统演进,可能需要调整模块划分结构(如增加新功能模块、合并相似模块),需要持续优化。
  • 实施建议:定期评审模块划分结构,根据系统演进情况,调整模块划分,确保架构的合理性。

三、总结收尾

通用应用逻辑公式

模块划分的通用逻辑可以总结为:“研究需求→分层→划分模块→评审优化”

公式拆解

  1. 研究需求:通过研究上下文图和功能树,识别系统的外部实体和功能需求,为后续的分层和模块划分提供依据。这就像盖房子,先确定要盖什么样的房子(需求),再设计房子结构(架构)。
  2. 分层:通过粗粒度分层(如UI层、业务层、数据层),实现技术关注点分离。这解决了业务痛点"需要清晰的职责划分"。
  3. 划分模块:在分层的基础上,进行细粒度模块划分,通过分层细化、分区、通用模块分离、通用机制框架化等方法,实现细粒度模块划分。这解决了业务痛点"需要支持深度优先开发"。
  4. 评审优化:通过用例驱动的方法,评审和优化模块划分结构,确保模块划分的合理性和可维护性。这解决了业务痛点"模块划分不合理"。

通俗比喻:就像"盖房子的4步流程"——先研究需求(确定要盖什么样的房子),再分层(确定有几层楼),再划分模块(确定每个房间的功能),最后评审优化(检查房子是否合理)。

可落地的交付物

模块划分设计检查清单

  1. 研究需求

    • 研究了上下文图,识别了外部实体(外部用户、持久化存储、外部系统/硬件、时限触发)
    • 研究了功能树,识别了功能需求(功能类别、功能组、功能项)
    • 确保需求完整准确,为模块划分提供基础
  2. 分层

    • 进行了粗粒度分层(UI层、业务层、数据层等)
    • 实现了技术关注点分离
    • 为细粒度模块划分提供基础
  3. 划分模块

    • 进行了分层细化(Layer内细分Sub-layer)
    • 进行了分区(将功能组映射到功能模块)
    • 分离了通用模块(所有功能模块都需要的基础服务)
    • 框架化了通用机制(数据库管理机制、事件通知机制等)
  4. 评审优化

    • 评审了模块职责清晰性(每个模块职责单一,没有职责重叠)
    • 评审了模块协作链完整性(每个功能模块都有完整的协作链)
    • 评审了通用模块复用性(通用模块被多个功能模块使用,但不依赖特定的功能模块)
    • 使用了用例驱动方法进行评审优化

最简操作模板

模块划分四步走:
1. 研究需求:研究上下文图(识别外部实体)和研究功能树(识别功能需求)
2. 分层:进行粗粒度分层,实现技术关注点分离
3. 划分模块:进行细粒度模块划分(分层细化、分区、通用模块分离、通用机制框架化)
4. 评审优化:用例驱动的模块划分结构评审、优化

回答核心问题

问题1:为什么需要系统化的模块划分方法?

核心原因

  • 避免随意设计:如果按照"想到哪,切到哪"的方式划分模块,容易导致模块职责不清、模块间耦合度高、无法支持深度优先开发。
  • 支持深度优先开发:只有通过系统化的方法,识别细粒度模块,形成模块协作链,才能支持深度优先开发——先开发一个完整功能,再开发另一个功能。
  • 提高架构质量:系统化的方法能够确保模块划分的合理性和可维护性,提高架构质量。

适用场景

  • 需要系统化方法:所有系统都应该使用系统化的方法,避免随意划分。
  • EDD方法适用:系统复杂度高,需要支持深度优先开发;需要支持迭代开发。

问题2:如何实现从粗粒度到细粒度的模块划分?

实现方法

  1. 分层细化:在粗粒度层内,进一步细化职责,形成子层(Sub-layer)。
  2. 分区:将"层"内支持不同功能组的职责分别"封装"到"细粒度模块"中,从而使每个"粗粒度功能模块"由一组位于不同"层"的"细粒度模块"组成。
  3. 通用模块分离:将通用模块(如错误报告、日志记录、安全验证)独立出来,供多个功能模块使用,但不依赖特定的功能模块。
  4. 通用机制框架化:将通用机制(如数据库管理机制、事件通知机制、事务服务机制)框架化,通过回调机制实现扩展。

实现原则

  • 同时考虑水平切分和垂直切分:系统需要处理多种技术关注点和多种业务功能,需要同时考虑水平切分(分层)和垂直切分(分区)。
  • 识别细粒度模块:只有识别了细粒度模块,才能形成模块协作链,支持深度优先开发。
  • 支持迭代开发:基于细粒度模块的协作链,可以进行"深度优先"式开发,实现快速交付价值。

记住:模块划分是架构师的"看家本领"。通过EDD方法,从需求出发,通过系统化的4个步骤(研究需求、分层、划分模块、评审优化),实现从粗粒度到细粒度的模块划分,避免"想到哪,切到哪"的随意设计,支持深度优先开发和迭代开发,让大型系统变得可管理。正如温昱所说:“有没有方法,决定一个架构师能走多远。”

参考:
《软件架构设计》温昱

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

roman_日积跬步-终至千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值