分层架构中的反向通知机制:基于接口的解耦实践
近期写了个小项目有感,这块虽然很简单, 但是值得深挖下设计思想,在后端系统设计中,分层架构是保障代码可维护性的常用模式。以"K8S错峰控制器"为例,我们将系统划分为三层核心模块,通过接口实现严格的单向依赖:
- ResourceManager(资源层) :封装K8s资源操作,暴露
resource.Manager
接口。 - StrategyManager(策略层) :依赖
resource.Manager
管理错峰策略,对外提供strategy.Manager
接口。 - Dispatcher(调度层) :基于
strategy.Manager
执行调度逻辑,暴露dispatcher.Dispatcher
接口。
三层形成明确的依赖链:ResourceManager → StrategyManager → Dispatcher。每层仅依赖下层接口,确保替换实现时无需修改上层代码。
需求冲突:策略更新需反向通知
当StrategyManager中的策略发生变更时,需即时通知Dispatcher重载配置。若直接在StrategyManager中引入dispatcher.Dispatcher
接口,会形成双向依赖(StrategyManager ←→ Dispatcher),破坏分层原则,导致模块耦合度上升、扩展性下降。
解决方案:接口抽象实现反向通信
为在保持分层架构的同时实现反向通知,采用"接口订阅"模式:
-
定义通知接口
在StrategyManager所属的
strategy
包中,定义ReloadNotifier
接口,描述策略更新的通知契约:go// strategy/notifier.go type ReloadNotifier interface { NotifyReload() // 触发策略重载 }
-
调度层实现接口
让Dispatcher的具体实现
dispatcherImpl
同时实现两个接口:- 自身业务接口
dispatcher.Dispatcher
(提供调度能力) - 策略层的
ReloadNotifier
(接收更新通知)
go// dispatcher/dispatcher.go type dispatcherImpl struct{} // 实现调度接口 func (d *dispatcherImpl) ReloadStrategy() { /* 重载逻辑 */ } // 实现通知接口 func (d *dispatcherImpl) NotifyReload() { d.ReloadStrategy() }
- 自身业务接口
-
初始化阶段绑定
系统启动时,将
dispatcherImpl
实例注册到StrategyManager:gostrategyMgr.SetReloadNotifier(dispatcherInstance)
效果:双向通信与单向依赖的平衡
该设计实现了:
- 保持原有分层依赖链不变,避免双向耦合。
- StrategyManager通过
ReloadNotifier
接口完成通知,无需知晓Dispatcher具体实现。 - 新增通知接收者或替换Dispatcher时,无需修改策略层代码,扩展性显著提升。
这种模式的核心是通过接口抽象隔离反向依赖,让下层模块主动适配上层的通知契约,既满足业务需求,又坚守分层架构的设计原则。