知识卡片
分层结构层层传递的约束:用冗余和轻微性能代价换取低复杂度
内容
分层结构还有一个典型特点是”层层传递”:一旦分层确定,业务流程必须严格按层依次传递,不能跨层跳跃——最简单的C/S结构里,用户必须先经过C层再传到S层,不能绕过C层直接访问S层;传统J2EE的4层架构,请求必须严格按分层顺序层层传递。这种约束把整体的依赖关系强制限定为相邻层之间的两两依赖(比如Business Layer只被Presentation Layer依赖、自己只依赖Persistence Layer),从而降低了整体系统的复杂度。但代价是冗余——不管业务逻辑多简单,每一层都必须参与处理,很多时候每层都要写一个几乎只是转发的简单包装函数。以一个”查看头像”这种极简功能为例:Presentation Layer里的AvatarView调用Business Layer的AvatarBizz.getAvatarUrl,AvatarBizz又原样调用Persistence Layer的AvatarDao.getAvatarUrl——三层的方法名和参数几乎一模一样,看起来很冗余。这时容易冒出一个想法:为了减少这种冗余,能不能自由选择绕过分层约束,比如让AvatarView直接调用AvatarDao?答案是不建议——分层架构的价值恰恰就体现在通过强制两两依赖限制住整体复杂度,一旦开了绕过分层的口子,时间一长架构必然变得混乱(比如Presentation Layer直接访问Persistence Layer、Business Layer直接访问Database Layer),到那时分层架构原本”扩展时能控制影响范围”这个核心价值就彻底丧失了,牵一发动全身、无法支撑快速扩展的问题又会卷土重来。而且虽然分层实现看起来啰嗦,单层本身的复杂度其实很低——像AvatarBizz.getAvatarUrl这种转发函数实现起来毫无难度,不会真正增加多少工作量。分层架构的另一个理论上的缺点是性能——每次业务请求都要穿越所有分层,多少存在一些性能浪费;但这个性能损失如果放在硬件性能孱弱的年代(比如20世纪80年代)可能比较明显,放到现在硬件和网络性能已经有了质的飞跃,绝大多数场景下这点理论上的性能损失完全可以忽略不计。