从 UI 到内核:Oinone 的全栈可扩展方法论 — Hook/AOP/扩展点与 SPI 的实践
Oinone 把“每个需求都是一个独立模块、支持各业务线差异化扩展、从框架到组件可深度扩展”作为设计目标。体系由 四条纵向能力(交互、模型、函数、应用生命周期)与 两条横向机制(API 生命周期扩展、平台 SPI 扩展)构成,通过字段/函数重写、Hook/AOP、扩展点(前置/后置/覆盖)与主题/母版/视图/路由/动作等粒度化插拔,做到既能快速装配,也能深入定制。
1. 设计目标
- 模块化:每个需求都能被打包成独立模块,独立发布与回滚。
- 差异化:同一能力在不同业务线可走不同实现与配置。
- 深度扩展:不仅页面可配,模型、函数、生命周期乃至平台内核都可扩展。
| 演示环境 | 相关视频 |
|---|---|
| ⚡ 直达演示环境
☕ 账号:admin ☕ 密码:admin |
🎬 1. [数式Oinone] #产品化演示# 后端研发与无代码辅助
🎬 2. [数式Oinone] #产品化演示# 前端开发 🎬 3. [数式Oinone] #个性化二开# 后端逻辑 🎬 4. [数式Oinone] #个性化二开# 前端交互 🎬 5. [数式Oinone] #个性化二开# 无代码模式 |
2. 总体框架
Oinone 的扩展体系可以概括为:
-
四条纵向能力
- 交互可扩展:主题、母版(布局)、视图、字段组件、路由、动作均可增加/替换/指定/组合。
- 模型可扩展:支持字段重写、增强模型与五类继承(抽象继承、扩展继承、代理继承、多表继承、临时继承)。
- 函数可扩展:函数重写、Hook 机制(AOP)、扩展点机制(前置/后置/覆盖)。
- 应用生命周期可扩展:模块安装/升级/重启逻辑,以及构建前/构建后、生命周期前/生命周期后的处理逻辑与元数据编排。
-
两条横向机制
- 基于 API 的生命周期扩展:对应用层事件(安装、升级、重启等)开放 API,让模块声明自己的编排与执行。
- 基于 SPI 的平台能力扩展:网络协议层 SPI、驱动层 SPI、FaaS 层 SPI(执行约束 SPI、RPC 调用 SPI)、持久层 SPI、ORM 层 SPI 等,允许替换/新增底座能力。
3. 交互可扩展:把“界面设计”拆到最小颗粒
从“看得见”的地方降本增效,是扩展的第一现场。
- 主题可扩展:风格可增加/替换/切换,适配多租户、多品牌。
- 母版(布局)可扩展:页面框架可替换/指定,如后台与大屏使用不同母版。
- 视图可扩展:列表/表单/看板/图表等视图类型可增补,与数据模型绑定。
- 字段可扩展:字段组件可增加/替换/指定,如把文本框替换成带校验的选择器。
- 路由可扩展:路由表项可增加/替换,模块自带路径不必侵入主工程。
- 动作可扩展:动作可增加/替换/指定/组合,支持将“审批+发运”等复合动作编排为一步。
示例(片段)
{
"module": "po-approval",
"views": {
"PurchaseOrder.Detail": {
"fields": {
"expectedDate": { "component": "DatePicker", "override": true }
},
"actions": [
{ "id": "approveAndShip", "type": "compose", "steps": ["approve", "ship"] }
],
"routes": [
{ "path": "/po/:id/ship", "component": "ShipView" }
]
}
},
"theme": { "extends": ["theme:default"] }
}
4. 模型可扩展:差异只写在差异处
模型层提供结构与行为的“正交”扩展:
-
字段重写:允许对已有字段的校验、渲染器、默认值进行重写。
-
增强模型:基于搜索/索引的查询增强、校验规则增强、派生字段等。
-
五类继承
- 抽象继承:提取公共字段/方法的抽象基类,不可直接实例化。
- 扩展继承:在不改变基类语义的前提下,向目标模型增量加字段/约束。
- 代理继承:行为转发/包装到另一个模型,便于隔离合规或跨域访问。
- 多表继承:一个逻辑模型映射多张表,支持冷热分层/历史归档。
- 临时继承:按租户/活动在运行期注入短期字段/规则,撤销即回退。
示例(伪 DSL)
// 在 BaseOrder 上做扩展继承
model PurchaseOrder extends BaseOrder with ExtendsInheritance {
@override field expectedDate: Date
field supplierRating: Int?
enhance {
searchIndex "supplier_name, po_no, sku_code"
}
}
5. 函数可扩展:Hook + AOP + 扩展点
函数层聚焦“时机与顺序”的控制:
-
函数重写(Override):在不改主流程签名的情况下,替换实现。
-
Hook 机制(AOP 能力):在切面前/后/异常挂载逻辑,跨越模块复用。
-
扩展点机制
- 前置扩展点:在核心逻辑运行前拦截/补充数据。
- 后置扩展点:在核心逻辑完成后做派生动作(审计、通知等)。
- 覆盖扩展点:完全取代默认实现。
示例(TypeScript 伪代码)
export const hooks = registerHooks('po')({
before('submit', async (ctx) => { await verifyBudget(ctx) }),
after('submit', async (ctx, result) => { audit(ctx, result) }),
override('calculateTax', async (ctx) => calcRegionalTax(ctx)) // 覆盖式扩展
})
6. 应用生命周期扩展:模块的一等公民地位
针对“安装/升级/重启”等事件,模块可以声明自己的执行逻辑;针对“构建前/构建后、生命周期前/生命周期后”,平台提供钩子做元数据编排与系统级处理。
- onInstall / onUpgrade / onRestart
- beforeBuild / afterBuild
- beforeLifecycle / afterLifecycle
- 元数据编排:将模型、视图、路由、动作、Hook 等产物做依赖拓扑排序,生成稳定装配序列(支持幂等与回滚)。
示例
export const lifecycle = {
async onInstall(ctx) { await seedDicts(ctx) },
async onUpgrade(ctx) { await migrate('v2->v3', ctx) },
async beforeBuild(ctx) { validateMetadata(ctx.graph) },
async afterBuild(ctx) { warmupCaches() }
}
7. 平台自身能力 SPI:内核也能被替换
当差异需要下沉到底座时,使用 SPI(Service Provider Interface):
- 网络协议层 SPI:自定义编解码/传输策略。
- 驱动层 SPI:接入自研/第三方驱动(消息、存储、设备)。
- FaaS 层 SPI:执行约束 SPI(限流/超时/重试)与 RPC 调用 SPI。
- 持久层 SPI 与 ORM 层 SPI:数据源路由、审计字段、软删、分片等。
示例
export interface StorageSPI {
put(object: Buffer, meta: Meta): Promise<URL>
get(url: URL): Promise<Buffer>
}
@Spi('storage')
export class MinIOStorage implements StorageSPI { /* ... */ }
SPI 的价值在于:当“函数扩展不够用”时,仍可在标准接口之下替换底座实现,而无需修改上层业务代码。
8. 元数据编排:把碎片装配成系统
Oinone 将模型/视图/路由/动作/Hook/SPI等产物抽象为统一元数据,通过声明式依赖与编排规则完成构建与运行期装配:
- 依赖声明:
dependsOn: [user, workflow@>=2.1] - 装配顺序:
order: [model, view, hook] - 作用域/租户选择器:
tenantSelector: ["region==EMEA"] - 冲突解决:优先级、命名空间、策略(覆盖/合并/忽略)
- 可回滚:每一步装配都有幂等检查与反向操作
示例(module.yaml)
name: po-approval
version: 1.3.0
dependsOn:
- user
- workflow@>=2.1
order: [model, view, hook]
guards:
tenantSelector: ["region==EMEA"]
9. 典型落地场景
-
多业务线的差异表单
- 用“字段重写 + 视图替换”实现 B2B 与零售两套表单,同步复用校验 Hook。
-
合规化审计
- 在“后置扩展点”统一落审计日志,接入“持久层 SPI”发送到冷存。
-
跨地域税则
- 覆盖
calculateTax函数,在租户维度启用差异化策略。
- 覆盖
-
活动期临时字段
- 通过“临时继承”与“租户选择器”在活动期注入派生字段,活动结束即回收。
-
替换存储底座
- 基于 “StorageSPI” 从本地盘切换到对象存储,无需业务改动。
10. 工程化与治理
- 版本与灰度:模块语义化版本 + 兼容性检查;按租户/环境灰度装配。
- 隔离性:命名空间、权限最小化;动作与 Hook 执行的资源限额。
- 可观测性:对每个扩展点输出指标/日志/追踪,如
hook.duration、override.count。 - 安全:Hook 的输入输出做 Schema 校验;SPI 提供沙箱/白名单。
- 稳定性:扩展点默认要幂等;提供熔断/降级与开关管理。
- 开发体验:热重载、扩展点可视化、冲突检测与一键回滚。
11. 心智模型:三板斧
- 先装配,后编码:能用元数据/编排解决的,优先不用代码。
- 优先 Hook,慎用 Override:Hook 可叠加且更安全,Override 只在语义确实需要替换时使用。
- API→SPI 的下沉路径:当应用层扩展不够用,再通过 SPI 下沉到底座。
Oinone 把“可扩展”做成了一套从 UI 到模型、从函数到生命周期、再到平台内核的完整方法论。通过 API 生命周期扩展 + 平台 SPI 扩展 两条通道,叠加 字段/函数重写、Hook/AOP、扩展点 与 主题/视图/路由/动作 等颗粒化插拔,既能支撑快速装配,又能承载深度定制。最终让开发者只写差异,让系统在复杂业务下稳定演进。

