01 — 保证
架构活在代码里。
不在脑子里,也不在文档里。
系统的消亡不是因为缺陷,而是因为随时间流逝逐渐不再被人理解,团队在换,复杂度在涨。我们学的是写代码、套用模式、遵循风格,却没人教我们如何驾驭复杂度,让系统在一年、两年、五年后依然可读。每一分钟花在搞清楚"这里到底在发生什么"上,都是从系统演进中偷走的一分钟。
AOA 走的是另一条路:把关于架构与系统的知识,从人的头脑和过时的文档,搬进代码——化为机器可读的意图:可执行、可校验、可观测。
一个类,一整个操作。
角色、步骤、补偿、错误、缓存与依赖都声明在同一处:Params 进,Result 出,中间没有任何侧门。
Action X-Ray。
查看一次操作中每个 Aspect 的输入、输出与耗时——无论开发、测试还是生产,都无需在业务逻辑中加入可观测性代码。
打开 Action X-Ray ↗六边形架构的端口,而非依赖。
@depends / @connection / @context_requires 声明 Action 的端口,适配器从外部接入。box.resolve(T) 只返回已声明的内容。
替换世界,而非代码。
真实的流水线原封不动——只是运行在一个被替换的世界里:用户、mock、上下文都可以换掉。这里测试通过的,就是最终会在生产环境中运行的那套代码。
02 — Maxitor
上面的图是真实的。
这就是绘制它的工具。
每一个 Domain、角色和依赖,在被导入的那一刻就会成为一个节点——同一套运行中系统的四种视图,全部都是未经编辑的原始导出。
03 — 能力
不只是一条流水线。
同一张被声明出来的图,同时为缓存、业务事件、可观测性与 agent 框架提供支撑——不需要一行胶水代码。
执行它们的那台机器。
每个 Action 都由同一台机器执行:角色、缓存、事件与回滚都是它做的。
缓存,声明式的。
cache_key 命中时直接短路到结果;未命中时运行该 aspect,并由 on_cache_write 写入缓存。
事件是事实,不是日志行。
box.info(Channel.business, …) 会发出一个带类型的事实——Maxitor、OCEL 与看板都能读取它。
Observer 无法干预。
移除所有 observer,行为不会有任何变化。遥测是一种保证,而不是一种期望。
错误只汇聚到一处。
唯一的流水线末端处理器决定操作在失败后返回什么。
Saga 会自行回滚。
失败时已完成的步骤按相反顺序撤销,回滚声明在它撤销的那一步旁。
先 RBAC,再 ABAC,层层深入。
check_roles 是纯粹的 RBAC:是否拥有该角色。grant.when / guard= 是按调用的轻量级 ABAC。access_decide() 是复杂 ABAC:对象与事实。
Domain 与 Role 是系统的地图。
Domain 是操作的组装点:图、访问矩阵、可视化。Role 通过继承承载权限。二者共同构成这张矩阵。
04 — 扩展
核心很小。
生态,可不小。
同一张声明出的图,把操作接入外部世界:智能体框架、队列、界面、可观测性。核心不会变大,变大的是它周围的一切。
LangGraph 中的一个带类型节点。
LangGraphController 把一个 Action 接入 agent 图中——契约不变,回滚机制不变。
一个 Action。任意前端。
一个 HTTP 路由与一次 AI 工具调用,本质上是同一次操作,拥有同样的保证。
OCEL/OCPM:真实的流程,而非图表。
每次运行都已经产生 lifecycle events。流程挖掘能从中看到真实的流程与涉及的实体,而非一次性画好的图表。
OTel:同样的事件,化作轨迹。
驱动 Action X-Ray 在线投影的同一个插件,也会把事件发送给 OpenTelemetry:同一个数据源,轨迹与实时运行,一次到位。
基于意图的 UI:不是布尔值,而是判定。
路由 → 类型化的能力:verdict() 解释拒绝原因,call() 执行调用。一份契约:React、移动端、Flet。
数字孪生
Actom 在一个声明中统一了实体、行为与智能体,即一个带状态、可通过 Action X-Ray 观测的行动者。文件由此成为系统的图态数字孪生体。
05 — 概念
五个主题。
每个都会成为独立页面。
五个独立视角,聚焦同一个系统:从声明语法,到 AOA 在其他框架中的定位。
01
强制语法
语法把每条规则都变成声明(@check_roles、@depends、校验手段、命名规则);导入时这些声明汇聚成一张图,于是代码即是规约,图即其机器可读形式:启动时校验、在 Maxitor 中绘制、由 Action X-Ray 追踪,而非游离于代码之外的文档。
02
AOA 宪法
八个原语和一组强制声明,不是代码风格,而是机器在每次导入时校验的法则。谁都无法悄悄违反它:不一致会直接阻止启动,而不是等到生产环境才暴露出来。
03
业务逻辑与基础设施的分离
Action 了解 Domain 与规则;如何访问 Postgres、Kafka 或第三方 API,则由 Gateway 或 Resource 接口背后的适配器负责。业务代码不直接导入驱动,边界由声明划定,而非开发者之间的约定。
04
意图导向编程的原则
代码声明的是意图,即应该发生什么、在什么条件下发生,而不是对具体库的一连串调用。实现通过端口(@depends、@connection)注入,只要意图不变,实现就可以更换。
05
与其他框架的对比
FastAPI 和 Django 提供路由和 ORM,但不会在导入时校验角色、依赖与状态迁移。LangGraph 确实显式构建图,但服务的是 LLM 步骤编排,而非业务操作。AOA 同时更接近二者:一张可执行的应用架构图,而不仅仅是传输层或模型流水线。
06 — 实用食谱
一个任务。
一份食谱。
面向任务,而非面向教程——你已经知道自己要构建什么,这些食谱告诉你该怎么做。
Action 的骨架:Params、aspect 流水线、checker、connection。
一份决策指南,帮你找到真正契合你所构建之物的原语。
把一个数据库、队列或 SDK 封装为一个受管理、可注入的依赖。
在不改动现有 Action 的前提下,以新协议对外暴露它们。
为 cache_key / on_cache_write 接入不同的缓存后端。
观测机器的生命周期事件,但无权干预。
把自定义的认证方案接入 @check_roles。
把 box.info / box.warning 路由到你实际存放日志的地方。
声明一套新的参与语法,供其他原语选择接入。
为 Action 能看到的调用信息,添加你自己的切片。
把已有代码库逐步迁移进来,一个 Action 接一个 Action。
07 — faq
问题 — 答案。
这不就是多加了几个装饰器的 Clean Architecture 吗?
区别在于强制执行。Clean Architecture 是一套需要由审阅者手动核对的约定。AOA 的契约——@check_roles、@depends、state checker——是由机器在操作运行之前检查的,而不是靠谁碰巧记得去看一眼。
我必须重写现有的 FastAPI 或 Django 应用吗?
不需要。aoa-fastapi-adapter 会把现有的 Action 以普通路由的形式暴露出来。你可以按 Action 逐个迁移,而不是按框架整体迁移。
aspect 流水线在运行时的开销是多少?
每个 aspect 都是一次普通的异步调用——没有序列化,也没有进程边界。开销来自你自己要求的那些检查,而不是框架本身。
使用 AOA 一定需要 Maxitor 吗?
不需要。Maxitor 读取的正是你已经声明好的 Domain、Role 与 Action——它是一个查看器,而不是一个依赖项。没有它,AOA 照样运行。
@compensate 和数据库事务有什么区别?
事务回滚的是单一数据存储。@compensate 回滚的是一整个业务操作,它可能触及了支付网关、邮件服务和数据库——而这些系统之间并不共享同一个事务边界。
什么时候 AOA 不是合适的选择?
对于一个脚本、一次性的 CLI 工具,或者只有单一操作且没有合规要求的服务来说,普通函数才是正确的选择。
⭐ 如果你喜欢 AOA,欢迎在 GitHub 上点亮 star,并在 GitHub Discussions 中加入讨论。
