耦合是指两个或两个以上的体系或两种运动形式间通过相互作用而彼此影响以至联合起来的现象。
解耦就是用数学方法将两种运动分离开来处理问题,常用解耦方法就是忽略或简化对所研究问题影响较小的一种运动,只分析主要的运动。
而对于软件架构设计中模块间的解耦或者说松耦合,则需要包括两个层面的含义,拿A,B两个模块来举例。第一个层面的解耦是指A不用了解到B模块内部的细节,B模块内部细节的变化不会影响到A模块对B模块能力和服务的消费;第二个层面则是A模块的完整运行不受到B模块任何状态变化的影响,即使是B模块完全失效也不至于影响到A模块本身的业务。对于第一个层面重点是模块本身的高内聚,松耦合划分,服务层的设计,通过服务层来完成模块间的解耦;对于第二个层面则重点是消息中间件,异步和事件驱动机制。所以在进行组件化架构和解耦的设计时候一定要关注到我们真正关注的目标。
不是模块间调用通过服务进行就是解耦,如果两个模块间有大量的服务调用和交互,两个模块的本质仍然是紧耦合,而且大量使用服务交互反而带来大量性能消耗。出现这种情况的核心原因还是在于我们进行业务模块划分和组件化设计的时候对业务流程,业务场景考虑的不足,导致模块本身结构划分不合理导致。
为了达到B模块内部细节的变化不会影响到A模块,不是简单的封装和暴露服务的问题,而是在架构设计上是否考虑可扩展性的问题。在面向接口的设计思路下面,AB两个模块间相互影响只应该体现在接口层面,只要接口本身不出现变化,则AB间相互不应该出现大的影响。面向接口设计下真正实现了将接口契约的定义和接口的实现彻底分离,实现变化不影响到接口契约,自然不影响到基于接口的交互。
对于A,B两个模块,如果前期的模块划分出现过多的AB模块相互调用和影响的情况,在整个设计上可能存在不合理的地方。双向交互和调用比单向交互耦合性更强,更难以真正的解耦,同时由于模块间存在的这种复杂依赖关系也增加了模块间集成的复杂度。在这种模式下最好的方式还是将共性依赖部分移出到第三方模块组件,即AB间的相互依赖转换为AB同时对底层C模块的依赖。这种迁移本质可以看作是各个模块内可复用内容的抽取,基础平台层的构建基础。
模块间的解耦,是贯彻整个模块多层架构的,即需要考虑从展现层,逻辑层,数据层到数据库的彻底解耦。很多时候我们在进行模块化设计的时候,发现展现和逻辑层解耦开了,相互直接没有API调用,结果实际审查的时候发现大量交互都转换到数据库层交互,这反而是使两个模块出现了更强的耦合性。只要在底层数据库彻底封闭的时候,我们在设计中往往才会考虑业务组件本身交互和接口,在业务组件和领域服务层需要设计和开放的服务。数据库完全不隔离,也是直接导致前面说过的贫血的领域服务层的一个重要原因。
对于一个完整的业务系统构成,可以看到在考虑平台层的时候,整个应用架构底层是一个共性的平台层,在共享的平台层上面才是各个业务模块。平台层作为一个最底层的支撑,本身和业务模块间就是一个紧耦合的关系,任何一个业务模块不可能脱离平台层而单独存在。而对于平台层上面的业务模块,则应该是一种松耦合的关系,可以按照最终用户的需求进行上层业务模块的配置的组装,这即是一个比较理想的组件化架构情况。
对于第二个层面的解耦,即A模块的完整运行不受到B模块任何状态变化的影响,这个往往需要通过消息中间件,异步消息机制来实现。而这仅仅是技术实现层面的事情。更加重要的是EDA事件驱动架构思想在业务建模和架构设计中的使用。EDA思想不是对传统业务建模和OO设计思想的否定,而是其关注重心从传统的业务流程,业务对象朝业务事件和状态的变化。事件的识别,分析,设计,开发和测试存在一个完整的生命周期,这个设计思想会对我们传统的同步模式下的设计方法造成大的影响。
要做到这一点可以看到在进行AB模块间交互和接口设计的时候,AB两个模块间交互的接口或服务都需要由传统的同步接口或服务转换为异步消息模式。即AB两个模块间的交互应该更多的是数据推送模式,而不是实时的拉式查询模式,这对于我们在服务设计上也提出了相应的要求。在这种架构下,消息中间件本身的可靠性将直接影响到业务模块的可靠性。
能否真正的做到模块间在第二个层面的彻底解耦,以及这种解耦的必要性和付出的代价,将是后续重要思考的一个问题和方向。
青春就应该这样绽放 游戏测试:三国时期谁是你最好的兄弟!! 你不得不信的星座秘密