引言:每一个复杂页面的背后,都有一场架构的战争
当一个业务页面变得日益复杂时,我们的代码维护工作,就像一场没有硝烟的战争。我们都曾深陷泥潭,与混乱的逻辑、失控的依赖作斗争。本文将分享我们团购业务鸿蒙团队的「破局之路」:如何摒弃传统架构的弊病,设计并实现了一套以「指挥链」为核心的现代化协同作战架构,最终将一个复杂的页面,拆解成了一支战斗力爆表的方面军。
一、起点:所有团队都经历过的「巨石梦魇」
我们先从起点说起,这也是我相信在座各位很多团队都经历过的痛苦:「巨石梦魇」。
在项目初期,为了快速迭代,我们最容易写出这样的代码------巨石组件 (God Object Component)。
它长啥样?
- 一个 .ets 文件,动辄成百上千行。
- UI 渲染、网络请求、状态管理、用户交互、埋点曝光......所有逻辑都像意大利面条一样搅在一起,密不可分。 让人难以辨清职责边界。
它的痛点,刀刀致命:
- 牵一发动全身:改个埋点,可能把 UI 搞崩了。
- 无法测试 :逻辑与 UI 深度 耦合,单元测试无从下手。
- 新人噩梦:新同学想看懂一个功能,得读完整个文件。
- 功能瓶颈:像「切 Tab 才曝光」这种依赖外部状态的需求,在这种架构下几乎无法优雅地实现。
二、歧路:看似解耦的「事件总线深渊」
为了解决「巨石」问题,很多团队会引入「事件总线 (Event Bus)」,试图通过它来解耦。
它长啥样?
一个全局的大喇叭。A 组件吼一嗓子,B、C、D 组件听到了就干活。

它带来了新的噩梦:
- 隐形依赖:代码表面毫无关系,背后全靠「吼」。数据流向完全失控,调试时如同追查幽灵。
- 命名地狱:事件名是裸字符串,极易冲突,难以管理。
- 内存泄漏:忘记取消订阅,页面关了,对象还在内存里「偷听广播」
三、破局:我们的「方面军」现代化协同作战架构
我们意识到,必须建立一套全新的秩序。我们的团购页面不是一个孤岛,而是主 App 里的一个重要战区。为了让它既能独立作战,又能和主 App 完美配合,我们设计了这套堪称典范的现代化军事架构------「方面军」架构。

这套架构通过清晰的层级划分、精确的职责定义,以及灵活的通信机制 ,将一个复杂的页面,变成了一支高度协同、反应迅速的精英部队。它解决了巨石组件的臃肿,规避了事件总线的混乱,让我们的代码真正实现高 内聚 、低 耦合 、可复用、可测试。
接下来,我们就深入了解这支「方面军」的每一个成员,看看它们是如何体现我们追求的架构之美与背后的设计原则。
第一站:大使馆 & 边防站 (GrouponTabNodeSlot)
- 他是谁? 主 App(总司令部)派驻在我们战区的「联络官」。
- 他干啥? 负责「接圣旨」和「发报」,将主框架的命令(如 Tab 切换)翻译成我们内部能听懂的「标准军用电码」(如 PAGE_SHOW),并广播出去。
- 一句话总结: 连接「中央」和「地方」的桥梁,负责翻译和转发命令。
- 体现的原则: 它作为一个明确的边界层,体现了职责单一原则( SRP ) ,仅负责外部事件的接收与内部事件的转换和分发,避免了与业务逻辑的耦合。
第二站:方面军指挥部 (GrouponTabComponent)
- 他是谁? 整个团购页面的最高指挥官,「大脑中枢」。
- 他干啥? 负责「建军」(创建 ViewModel、Plugin、Context)、「排兵布阵」(决定页面整体 UI 布局)、「监听电台」(接收上游命令并分发)。
- 一句话总结: 建制、布局和命令中转的核心,所有部队的直属上级。
- 体现的原则: 它专注于页面整体的组装和协调,同样遵循了职责单一原则( SRP ) ,不深入具体的业务逻辑实现,保持了自身的纯粹性。
第三站:多功能战术背包 (GrouponContext)
-
他是什么? 一个神奇的背包,指挥部会给进入战区的每一支部队都配发一个,里面装满了标准装备。
-
它包含哪些标准装备?
- 「指挥官电话」 (getHomepageViewModel): 随时联系参谋总长。
- 「弹药库地图」 (getDataCenter): 知道去哪领共享数据。
- 「友军频道」 (getScopedInstanceRegistry): 安全地找到其他部队的「大脑」,实现依赖查找。
- 「技能面板」 (registerAbility): 共享和调用其他部队的特殊能力。
-
一句话总结: 解耦的灵魂,让各部队既能共享资源,又能互不依赖,极大提升了灵活性和可维护性。
-
体现的原则:
Context
的设计是 ****依赖倒置原则 ( DIP ) 的典范。它提供的是抽象接口,而不是具体的实现。这意味着高层模块(例如ViewModel
)不直接依赖低层模块(例如数据中心),而是两者都依赖于Context
这个抽象。这样,我们可以灵活替换具体的实现,模块间解耦,极大地提升了可测试性。
第四站:参谋总长 (GrouponHomepageViewModel)
- 他是谁? 「指挥部」任命的首席参谋,所有业务逻辑的「总规划师」。
- 他干啥? 他直接掌管着所有「精英部队司令」(子 ViewModel)的作战核心,负责制定整体作战计划(如刷新页面数据),并将任务精准分发。他会直接命令各司令更新各自部队的数据。同时,为了避免自身膨胀,对于非 UI 渲染类的跨模块命令(如页面生命周期事件),他会委托「通信部长」(GrouponRootPlugin)进行间接驱动。
- 一句话总结: 所有 ViewModel 的 ViewModel,业务逻辑的最高聚合点
- 体现的原则: 参谋总长是职责单一原则( SRP )的体现, 它专注于页面整体的业务逻辑协调和数据分发。同时,它的设计也巧妙地应用了开闭原则( OCP ) :对于核心的、结构稳定的数据更新,它保持直接调用,效率最高;而对于非数据更新类、可能经常变化的跨模块命令(如生命周期事件),它会委托给 "通信部队" (Plugin) 进行间接驱动。这使得参谋总长的主体代码能对修改关闭 ,而对新增的事件类型能通过
Plugin
机制对扩展开放,避免了自身方法的无限膨胀。
它不直接管理某个具体 UI 组件(部队)的状态,而是管理其他 ViewModel(部队的大脑/司令)。
一个常规的 ViewModel,比如 GrouponDiamondViewModel(金刚位司令),它的职责是管理「金刚位」这个具体组件(部队)的所有事情:数据是什么、什么时候曝光、UI 长什么样等等。它是一个分队司令。
而 GrouponHomepageViewModel(参谋总长),它的级别更高。它手下没有直接的兵(UI 组件),但它直接指挥着所有分队司令,同时通过通信机制协调其行动。
「业务逻辑的最高聚合点」 意思是:所有跨越多个组件的、页面级别的业务逻辑,都在它这里汇总和发起。
举个最典型的例子:下拉刷新整个页面。
命令下达: 用户下拉刷新,这个命令会传到「参谋总长」(GrouponHomepageViewModel) 这里。
制定计划: 「参谋总长」不会自己去画 UI,但它知道完成这个任务需要哪些步骤。它会说:"好的,我需要向总部(服务器)请求最新的全量作战数据。"
分发任务: 等到它从服务器拿回了一大包最新的数据后,它开始分发任务。它会把数据拆开:
- 对「金刚位司令」(GrouponDiamondViewModel) 说:"这是你的新数据,去更新你的部队!"
- 对「信息流司令」(MultiTabViewModel) 说:"这是你的新数据,去更新你的部队!"
- 对「头部司令」(HeaderViewModel) 说:"城市信息变了,更新你的部队!"
你看,它就像一个战役总指挥:
- 它不关心每个士兵怎么开枪(具体 UI 怎么渲染)。
- 它只负责发起整个战役(页面刷新),并向各个分队司令直接下达指令(针对数据更新)或通过通信部队间接下达指令(针对其他事件)。
- 所有需要多个部队协同作战的复杂任务,都在它这里统一协调。
第五站:通信部队 (GrouponRootPlugin & 子插件们)
- 他们是谁? RootPlugin 是通信部长,手下有一堆负责不同频道的「传令兵」(如
GrouponDiamondPlugin
)。 - 他们干啥? 实现专业的「发布-订阅」模式,当"参谋总长"或其他模块有特定命令 (如页面生命周期事件)需要分发时,会通知通信部长。通信部长再将命令精准分发给关心该命令的传令兵 (子插件)。传令兵还会将接收到的指令,传递给对应模块的司令( ViewModel ),或者直接委托给其内部的专业能力代理(Ability)进行后续处理。
- 一句话总结: 专业的事件分发系统,让指挥部不再关心命令的具体接收方,同时也是能力路由的关键通路。
- 体现的原则:
Plugin
机制是开闭原则( OCP ) 的典型应用。新增一种事件类型或一个关心此事件的模块,我们只需要增加新的Plugin
或少量配置,而不需要修改核心的RootPlugin
逻辑,对扩展开放,对修改关闭,使得系统极易扩展。它也体现了高内聚低耦合,将通信逻辑封装在一起,与业务逻辑解耦。
第六站:集团军司令 (XModeleViewModel)
-
他是谁? XX 集团军的司令,是整个 XXX 模块的状态管理者 和命令接收者。
-
他干啥? 他有两个主要职责:
- 直接接收 来自「参谋总长」的数据更新命令,管理自己模块的数据和 UI 状态。
- 接收 来自「传令兵」(Plugin)的其他命令(例如「tab 切换」)。
-
一句话总结: 各模块的门面,负责接收外部指令并委托专业能力进行决策。
-
体现的原则: 「司令」收到报告后,并不会立刻行动。这正是职责单一原则( SRP ) 的实践。他专注于管理自己模块的数据和 UI 状态,将复杂的业务决策(如曝光判断)委托给更专业的
Ability
,从而保持自身的纯粹性,让代码更易于理解和测试。
第七站:战术专家/能力代理 (XXXAbility)
- 他是谁? 分区司令任命的**「战术专家」**。一个集团可能有多个「战术专家」,他们拥有处理特定业务逻辑的专业能力。
- 他干啥? 比如金刚位决策专家
DiamondLynxCardAbility
,他是真正进行曝光决策的模块 。他会综合所有必要的「情报」(这些情报可能来源于 ViewModel 提供的 UI 可见性状态、Lynx 引擎是否就绪、曝光规则优先级等),并根据这些信息,最终决定是否执行曝光。他负责封装复杂的业务决策逻辑。 - 一句话总结: 封装业务决策逻辑的"大脑",根据各项条件决定是否执行命令。
- 体现的原则:
Ability
的引入是职责单一原则( SRP )的完美体现。 它将复杂的、非 UI 相关的业务逻辑(如曝光判定)从ViewModel
中彻底剥离出来,让ViewModel
能够更加专注于 UI 状态管理。当有新的曝光逻辑或判定规则时,我们只需修改或新增对应的Ability
,这完美符合开闭原则(OCP),对扩展开放,对修改关闭。
第八站:通信官 (GrouponAnnieXComponentViewModel)
这里举例金刚位卡片容器,其他类比
-
他是谁? 下属某个营级单位的专职军官。一个金刚位里可能有多张 Lynx 卡片,每一张卡片(一个营)都配有自己独立的「通信官」。
-
他干啥? 他的工作是技术性、战术性的。他接收来自「战术专家」(DiamondLynxCardAbility)的具体命令,比如「执行曝光」。
- 他接到命令后,不会再做决策,而是立刻去检查自己这个营的通信设备------也就是 Lynx 引擎------是否已经架设完毕 (
isLynxReady
)。 - 如果设备好了,他立刻通过电台把命令发给前线士兵(Lynx 前端)。
- 如果设备没好,他会把命令记在小本本上(
pendingEvents
),等设备一好,马上发送。
- 他接到命令后,不会再做决策,而是立刻去检查自己这个营的通信设备------也就是 Lynx 引擎------是否已经架设完毕 (
-
特点: 他不关心「为什么」要曝光,只关心「如何」把曝光这个命令在技术层面安全、可靠地发送出去。
-
体现的原则: 这同样是 职责单一原则( SRP ) 的体现。通信官只负责命令的最终技术执行和可靠性保障,将 Lynx 相关的技术细节封装在自己内部,与业务决策和 UI 状态管理完全解耦。
架构优势:高内聚、低耦合、可复用、可测试
- 指挥链清晰: 事件流向单向、可预测,调试易如反掌。
- 职责分离 : Plugin 传话,ViewModel 管理状态,Ability 决策,Component 渲染,各司其职,每个模块都遵循了职责单一原则。
- 依赖 解耦 : 通过 Context 实现依赖查找,而非直接创建,可测试性拉满,完美体现依赖倒置原则。
- 可扩展性强: 新增功能只需添加新的「部队」和「大脑」,对现有代码零侵入,完美符合开闭原则。
四:实战剖析:一次"切 Tab 曝光"的完整旅程
需求背景:
- 用户切换 Tab、主动刷新后,需上报曝光埋点。
- 常规的上下滑动不重复上报,进入二级页/后台返回等场景亦不上报。
下面,我们沿着指挥链,看看一次「切 Tab」事件,代码是如何一步步执行的。

第 1 步:边防站(GrouponTabNodeSlot)点燃烽火
根据主框架文档, 可以通过给主框架事件中心注册监听收到指令。GrouponTabNodeSlot
作为与主框架的接口,通过监听框架事件,将外部变化转化为内部的标准化生命周期事件。
typescript
@Component
export struct GrouponTabNodeSlot {
@State private lifecycleController: GrouponLifecycleController = new GrouponLifecycleController();
private mainStructureEventCallback: MainStructureEventCallback = (eventType, param) => {
let grouponEventType: GrouponLifecycleEventType | null = null;
let sourceType: SourceReasonType = SourceReasonType.NORMAL;
// ... 此处省略事件类型转换逻辑 ...
if (param?.isTabChange) {
sourceType = SourceReasonType.TOP_TAB
}
switch (eventType) {
case PageEventType.PageShow:
grouponEventType = GrouponLifecycleEventType.PAGE_SHOW;
break;
// ...
}
if (grouponEventType !== null) {
// 通过控制器,将标准化的事件广播出去
this.lifecycleController.dispatch(grouponEventType, { sourceType: sourceType });
}
}
aboutToAppear(): void {
// 注册监听
MainStructureManagerCenter.getInstance().getMainStructureEventManager().registerEvent(HomePage_Groupon, this.mainStructureEventCallback);
}
// ...
@Builder
buildGrouponComponent() {
// 将控制器传递给指挥部
GrouponTabComponent({
lifecycleController: this.lifecycleController,
})
}
}
第 2 步:指挥部 (GrouponTabComponent) 接收信号,分派任务
指挥部接收到信号后,立刻命令「通信部长」GrouponRootPlugin
将指令传达下去。
typescript
@Component
export struct GrouponTabComponent {
private rootPlugin: GrouponRootPlugin | null = null;
@Provide grouponContext?: GrouponContextImpl = undefined
@ObjectLink lifecycleController: GrouponLifecycleController;
private handleLifecycleChange(eventType: GrouponLifecycleEventType, params: GrouponLifecycleParams): void {
// 命令通信部长,向下分发事件
this.rootPlugin?.onLifecycleChanged(eventType, params)
}
aboutToAppear(): void {
this.rootPlugin = new GrouponRootPlugin(this.grouponContext);
// 设置监听,接收来自边防站的信号
this.lifecycleController.setListener((eventType, params) => this.handleLifecycleChange(eventType, params));
}
// ...
}
第 3 步:通信部长 (GrouponRootPlugin) 接收并分发,传令兵 (GrouponDiamondPlugin) 精准传达
通信部长 会根据事件类型,将命令分发给所有注册的传令兵(子插件)。
传令兵 (GrouponDiamondPlugin) 收到广播后,会判断自己是否关心这个事件。如果关心,它会立刻通知对应的金刚位司令(GrouponDiamondViewModel),告知其接收到了页面生命周期的变化。
- 级别: 集团军参谋部级别。
- 职责: 他的工作非常纯粹,就是传递命令。他站在指挥部外面,竖着耳朵听「战区总指挥」(页面生命周期)的广播。
- 当听到「全体注意,页面出现了!」这个宏观指令时,他会立刻跑去向他的直属领导 ------「金刚位集团军司令」GrouponDiamondViewModel 报告:"报告司令,上级命令,页面可见了!"
- 特点: 他不关心命令的具体内容是什么,也不关心司令听完报告后会做出什么决策。他只负责把 A 点的消息,准确无误地传到 B 点。
金刚位专属的「传令兵」收到广播后,立刻向自己的直属领导------「金刚位司令」报告。
csharp
// 这是在 GrouponRootPlugin 中发生的
this.plugins.forEach(plugin => plugin.onLifecycleChanged(eventType, params));
// 这是在 GrouponDiamondPlugin 中发生的
public onLifecycleChanged(eventType: GrouponLifecycleEventType, params: GrouponLifecycleParams): void {
// 监听到特定事件
if (eventType === GrouponLifecycleEventType.PAGE_SHOW && params.sourceType === SourceReasonType.TOP_TAB) {
// 通过 Context 找到司令,并调用其接口
this.diamondViewModel.triggerExposureCheck({ reason: 'tab_switch', show: true });
}
}
第 4 步:金刚位司令 (GrouponDiamondViewModel) 接收命令,委托决策
- 级别: 金刚位集团军的司令。
- 职责: 他是金刚位模块的状态管理者 和命令接收者 。他接收来自"传令兵"的命令(例如"页面可见"),但他太忙了,完全不能抽身直接做最终的曝光决策 。他会将这些业务指令和必要的状态 (如自身可见性、数据是否加载等)传递给金刚位卡片能力代理(DiamondLynxCardAbility) ,由其统一进行决策。
- 一句话总结:金刚位模块的门面,负责接收外部指令并委托专业能力进行决策。
「司令」收到报告后,并不会立刻行动,为遵循单一职责原则,他会召集手下得力干将「专人做专事」。此次警报为曝光警报,于是直接找到分管曝光事件的高级将领让其汇报决策。
typescript
// 入口:接收指令
public triggerExposureCheck(params: ExposureTrackParams): void {
hilog.info(0x0000, 'DiamondExposure', `🔑 收到生命周期事件: ${params.reason}`);
// 将指令委托给能力代理进行决策
this.diamondAbilities.forEach((ability: DiamondLynxCardAbility) =>
ability.triggerExposureCheck({ reason: 'tab_switch', show: true })
);
}
第 5 步:战术专家/能力代理 (DiamondLynxCardAbility)运筹帷幄,做出决策
- 级别: 金刚位司令任命的「战术专家」。一个金刚位里可能有多张 Lynx 卡片,每张卡片都对应一个
DiamondLynxCardAbility
。他拥有处理特定业务逻辑的专业能力。 - 职责: 他是真正进行曝光决策的模块。他会综合所有必要的"情报"(这些情报可能来源于 ViewModel 提供的 UI 可见性状态、Lynx 引擎是否就绪、组件是否在屏幕上可见、曝光规则优先级等),并根据这些信息,最终决定是否执行曝光。他负责封装复杂的业务决策逻辑。
- 一句话总结:封装 业务决策逻辑的"大脑",根据各项条件决定是否执行命令。
「战术专家」收到报告后,并不会立刻行动,而是根据自己掌握的全部信息(是否可见、内容是否就绪等)进入决策中枢进行判断。
kotlin
// 这是在 DiamondLynxCardAbility 中发生的决策逻辑
// Component 调用可见性
public updateExposureVisibility(isVisible: boolean): void {
const wasVisible = this.isCurrentlyVisible;
this.isCurrentlyVisible = isVisible;
if (this.isCurrentlyVisible && !wasVisible) {
hilog.info(0x0000, 'DiamondExposure', `🔑 组件变为可见`);
this.checkAndTriggerExposure({
reason: 'visible',
show: true,
});
}
}
// 入口:接收指令
public triggerExposureCheck(params: ExposureTrackParams): void {
hilog.info(0x0000, 'DiamondExposure', `🔑 收到生命周期事件: ${params.reason}`);
this.checkAndTriggerExposure(params);
}
private checkAndTriggerExposure(params: ExposureTrackParams): void {
// 1. 处理"离开"事件 (show: false)
// 离开事件只需要Lynx就绪即可发送,不受组件是否可见的限制。
if (!params.show) {
if (params.reason === 'tab_switch' && this.isLynxReady) {
hilog.info(0x0000, TAG, `✅ [${this.getCardIdentifier()}] Triggering Tab Switch HIDE exposure!`);
this.notifyFrontendExposure(params);
}
return;
}
// 2. 处理"进入"事件 (show: true),必须满足前置条件
if (!this.isLynxReady || !this.isComponentVisible) {
hilog.info(0x0000, TAG, `⏳ [${this.getCardIdentifier()}] Exposure condition not met for SHOW event (LynxReady: ${this.isLynxReady}, Visible: ${this.isComponentVisible})`);
return;
}
// 3. 优先处理待处理的刷新事件
if (this.hasPendingRefresh) {
hilog.info(0x0000, TAG, `✅ [${this.getCardIdentifier()}] Triggering PENDING REFRESH exposure!`);
this.notifyFrontendExposure({ reason: 'user_refresh', show: true });
this.hasPendingRefresh = false; // 消费掉标志
this.isFirstShow = false; // 刷新事件消耗首次曝光机会
return;
}
// 4. 处理首次曝光
if (this.isFirstShow) {
hilog.info(0x0000, TAG, `✅ [${this.getCardIdentifier()}] Triggering FIRST EVER exposure! Reason: ${params.reason}`);
this.notifyFrontendExposure(params);
this.isFirstShow = false;
return;
}
// 5. 处理后续的"进入"事件,如切Tab回来
if (params.reason === 'tab_switch') {
hilog.info(0x0000, TAG, `✅ [${this.getCardIdentifier()}] Triggering Tab Switch SHOW exposure!`);
this.notifyFrontendExposure(params);
}
}
// ... existing code ...
第 6 步:通信官 (GrouponAnnieXComponentViewModel) 确保命令最终执行
-
级别: 下属某个营级单位的专职军官。一个金刚位里可能有多张 Lynx 卡片,每一张卡片(一个营)都配有自己独立的「通信官」。
-
职责: 他的工作是技术性、战术性的。他接收来自"司令"的具体命令,比如"执行曝光"。
- 他接到命令后,不会再做决策,而是立刻去检查自己这个营的通信设备------也就是 Lynx 引擎------是否已经架设完毕 (isLynxReady)。
- 如果设备好了,他立刻通过电台把命令发给前线士兵(Lynx 前端)。
- 如果设备没好,他会把命令记在小本本上(pendingEvents),等设备一好,马上发送。
-
特点: 他不关心"为什么"要曝光,只关心"如何"把曝光这个命令在技术层面安全、可靠地发送出去。
战术专家(DiamondLynxCardAbility)的「曝光」命令下达后,由各卡片专属的「通信官」负责与底层 Lynx 引擎进行技术对接,确保命令在正确的时间被可靠地执行
csharp
public sendEventToLynx(event: LynxPendingEvent): void {
// 判断:士兵的装备(Lynx引擎)好了吗?
if (this.isLynxReady) {
// 好了,立刻发送
this.lynxComponentDelegate?.sendEvent(event.eventName, event.params)
} else {
// 没好,先记在小本本上(放入待处理邮箱)
this.pendingEvents.push(event)
}
}
public onLynxLoadSuccess(): void {
// 士兵报告装备完毕!
this.isLynxReady = true
// 清空小本本,把之前记下的命令全部发送出去
this.pendingEvents.forEach(event => {
this.sendEventToLynx(event)
})
this.pendingEvents = []
}
设计哲学:为什么「通信官」不关心 UI 是否可见,以及「能力代理」的意义?
这正是我们架构设计的单一职责原则 ( Single Responsibility Principle ) 和关注点分离的完美体现!
- 金刚位司令 (GrouponDiamondViewModel) :他的核心职责是管理 UI 状态 和接收来自上层的业务指令 。他知道组件是否可见 (
isCurrentlyVisible
)、数据是否加载好 (isContentLoaded
),并将这些状态同步给战术专家 。他自身不负责复杂的决策逻辑,而是将决策权委托 给更专业的Ability
,从而保持自身的纯粹性和职责聚焦。 - 战术专家 (DiamondLynxCardAbility) :他是业务决策的"大脑" 。他站在山顶上,拿着望远镜,观察战场态势(结合
ViewModel
提供的 UI 可见性、Lynx 引擎就绪状态等信息),并监听来自"司令"的命令。只有当"天时地利人和"都满足时,他才决定"开火!"。他负责封装复杂的曝光规则和优先级判断。 - 通信官 (GrouponAnnieXComponentViewModel) :他的职责是负责执行。他待在战壕里,不关心外面是否打雷下雨(UI 是否可见),也不关心"为什么"要曝光。他的唯一任务就是维护好和单个士兵的通信线路(与 Lynx 引擎的连接)。当"战术专家"的"开火"命令传来时,他要保证能把这个命令准确地传给士兵。如果士兵的枪还没组装好(Lynx 未就绪),他就先把命令记在小本本上,等士兵一弄好,立刻传达。
这种分层和职责划分,让每个模块都更纯粹、更内聚,也更易于测试和维护。Ability
的引入,使得复杂的业务决策逻辑可以从 ViewModel
中抽离,变成一个独立的、可复用的能力,进一步提升了架构的灵活性和可扩展性
五、结语:架构的价值,在于赋能未来
通过这套精密的架构,我们成功地将一个复杂的团购页面,拆解成了一系列高内聚、低耦合、可复用、可测试的独立作战单元。每个单元各司其职,通过标准化的接口和上下文高效协同。这不仅解决了当下的业务痛点,更重要的是,它为我们未来的快速迭代和功能扩展,打下了坚实而优雅的基础。这,就是技术的价值所在。