打开网易新闻 查看精彩图片

一个45+模块的NodeJS框架,正在把微服务开发的脏活累活打包成装饰器。HazelJS v0.4.0发布的三个新特性——分布式Saga、分布式锁、零配置文档——瞄准的都是同一个痛点:开发者的时间不该浪费在基础设施代码上。

这套组合拳打的是"声明式基础设施"的主意:用注解(装饰器)把复杂模式藏起来,让业务代码看起来像纯业务代码。

这不是新思路。Spring Boot干了十年,NestJS也在走这条路。但HazelJS的赌注在于:AI原生时代的框架,得让机器能读懂你的意图,而不只是人类。

从手写状态机到@SagaStep:分布式事务的两种死法

从手写状态机到@SagaStep:分布式事务的两种死法

微服务架构有个经典噩梦:用户下单成功,扣款成功,库存扣减失败。钱没了,货还在,用户懵了,客服炸了。

传统解法是用Saga模式——把长事务拆成本地事务链,每一步配一个补偿操作。出问题时倒着执行补偿,把系统状态"回滚"到一致。听起来优雅,写起来地狱。状态机代码动辄几百行,测试覆盖率永远上不去,半夜报警时你根本不敢动。

@hazeljs/saga提供了两条逃生通道。

编排式(Orchestration)像有个指挥家。你定义一个Saga类,用@SagaStep标记每个步骤,指定补偿方法。框架自动处理执行顺序、失败检测、补偿触发。代码长这样:

@Saga({ name: 'checkout-saga' }) export class CheckoutSaga { @SagaStep({ order: 1, compensate: 'cancelReservation' }) async reserveProduct(ctx: SagaContext) { return await this.inventory.reserve(ctx.input.productId); } @SagaStep({ order: 2, compensate: 'refund' }) async chargeUser(ctx: SagaContext) { return await this.payment.charge(ctx.input.userId, ctx.input.amount); } async cancelReservation(ctx: SagaContext) { await this.inventory.cancel(ctx.input.productId); } }

补偿逻辑和业务逻辑写在一起,失败时自动调用。你不需要维护一个独立的状态机,不需要手动发送补偿指令,框架帮你盯着。

编排式适合流程明确、步骤可控的场景。但微服务越来越多时,中央指挥会变成瓶颈。每个服务都要等"指挥家"发令,延迟和耦合度都会上升。

于是有了协作式(Choreography)。没有中央大脑,每个服务监听事件,完成自己的部分,发出下一个事件。失败时由相关服务自行触发补偿。

@Injectable() export class OrderService { @OnEvent('order.created') async handleOrderCreated(data: any) { // 自动参与分布式事务 await this.processOrder(data); } }

代码更薄,耦合更松。代价是全局流程散落在各个服务里,调试时需要翻遍代码才能拼出完整链路。HazelJS的解法是用事件总线自动关联事务上下文——服务不知道自己属于哪个Saga,但框架知道。

两种模式都支持,选哪个取决于你的组织架构。康威定律在这里依然生效:团队沟通顺畅用编排,服务边界清晰用协作。

@DistributedLock:Redis不是银弹,但够用了

@DistributedLock:Redis不是银弹,但够用了

第二个新包@hazeljs/distributed-lock解决的是更古老的问题:分布式环境下的互斥。

单机时代用mutex,多机时代用分布式锁。Redis的SETNX命令被用了十几年,但生产级的锁要考虑的事远不止"设置一个键":锁续期、主从切换时的脑裂、可重入性、公平性……

自己写一套可靠的Redlock实现,测试用例能写两百个。大多数团队的选择是:抄个大概,祈祷别出事。

HazelJS把这套封装成一个装饰器:

@Injectable() export class StockService { @DistributedLock({ key: 'update-stock-{{productId}}', ttl: 10000, wait: true }) async decrementStock(productId: string) { // 同一productId同时只有一个实例能执行 return await this.db.update(productIdId); } }

key支持模板语法,ttl防死锁,wait控制阻塞行为。Redis和内存后端可切换,本地开发不用起Redis实例。

这个设计的聪明之处在于:锁的粒度由业务参数决定,而不是写死在代码里。'update-stock-{{productId}}'意味着不同商品的库存操作互不阻塞,只有同一商品的并发请求才会排队。这比全局锁高效得多,也比手动拼接锁键安全得多——模板语法在编译期检查,拼错会报错,不会静默失败。

打开网易新闻 查看精彩图片

但Redis分布式锁的天花板很明显。Redis作者Antirez自己写过,Redlock在时钟漂移场景下不可靠。HazelJS没有试图解决这个问题,而是提供了内存后端选项——单节点部署时直接用进程内锁,避开分布式一致性难题。

这是务实的妥协。大多数NodeJS应用根本不到需要严格线性一致性的规模,Redis锁的"基本可用"已经足够。

generateAutoSpec:文档终于能自己长了

generateAutoSpec:文档终于能自己长了

第三个特性看似最轻量,却可能改变最多人的日常。

@hazeljs/swagger的generateAutoSpec()函数,扫描模块里的所有控制器,自动生成完整的OpenAPI 3.0规范。不需要@ApiOperation、@ApiResponse装饰器,不需要手动维护YAML文件。

import { SwaggerService } from '@hazeljs/swagger'; import { AppModule } from './app.module'; @Service() export class MyDocService { constructor(private readonly swagger: SwaggerService) {} getDocs() { // 发现AppModule中的所有控制器并构建规范 return this.swagger.generateAutoSpec(AppModule, { title: 'Auto-Generated API', version: '1.0.0' }); } }

原理不复杂:运行时反射读取控制器的路由、参数类型、返回类型,映射到OpenAPI结构。TypeScript的装饰元数据让这一切成为可能。

真正的价值不在"自动生成",而在"零配置"。NestJS的Swagger集成需要大量装饰器,SpringDoc也需要注解。HazelJS的赌注是:如果类型定义已经存在,为什么要重复写一遍?

代价是灵活性。自动生成的文档不会包含业务语义——参数叫"userId",文档里就是"userId",不会解释"这是下单用户的唯一标识"。需要精细控制时,依然可以用装饰器覆盖。

这对快速迭代的团队是合理的交易。API文档的敌人从不是"不够详细",而是"过期三个月没人敢信"。先让文档和代码同步,再考虑锦上添花。

45个模块的野心:AI原生框架长什么样

45个模块的野心:AI原生框架长什么样

把三个特性连起来看,HazelJS的路线图逐渐清晰。

分布式Saga让机器能理解业务流程的结构——哪些步骤是原子操作,哪些是补偿路径。分布式锁让机器能识别临界区,自动处理并发安全。零配置文档让机器能从代码中提取接口契约,无需人工标注。

这些能力叠加起来,指向同一个方向:框架正在积累"机器可读的业务知识"。

这是"AI原生"的真正含义。不是加个ChatGPT插件生成代码,而是让框架本身成为AI的协作界面。当AI助手需要理解你的系统时,它读的不是自然语言注释,是结构化的元数据:Saga定义、锁边界、API契约。

45个模块的规模也说明了策略:不做大而全的全家桶,而是每个垂直领域一个包。需要Saga就装@hazeljs/saga,需要锁就装@hazeljs/distributed-lock。这种模块化在NodeJS生态里是生存必需——依赖地狱能轻易杀死一个框架。

但碎片化也有代价。模块之间的版本协调、文档分散、学习曲线陡峭,都是社区治理的挑战。HazelJS目前还是相对年轻的项目,v0.4.0的版本号说明它尚未承诺稳定API。

对25-40岁的技术决策者,这个框架的评估维度很清晰:如果你正在用NodeJS构建微服务,厌倦了手写基础设施代码,且团队能接受装饰器驱动的开发风格,HazelJS值得一个POC(概念验证)。它的竞争对手不是Spring Cloud——那是另一个量级——而是NestJS的生态位:足够现代,足够轻量,足够TypeScript。

区别在于NestJS背靠大公司(Kamil Myśliwiec全职维护,有企业赞助),生态更成熟;HazelJS更激进,AI原生的叙事也更贴合当前风口。选哪个,取决于你对"稳定"和"前瞻"的权重分配。

一个细节值得注意:v0.4.0的发布说明里完全没有提到性能基准测试。分布式锁的延迟、Saga协调器的吞吐、文档生成的耗时——这些生产环境的关键指标,目前还是空白。这是小团队的资源限制,也是早期采用者需要自行验证的风险点。

框架作者显然知道这一点。整个发布文档的语气是克制的,没有"革命性""颠覆性"的修辞,只有功能描述和代码示例。这种克制本身是一种信号:他们知道自己在哪个阶段,没打算透支信任。

NodeJS的框架史是一部"足够好就赢了"的历史。Express因为简单赢了Koa的优雅,NestJS因为企业友好赢了Fastify的性能。HazelJS的赌注是:AI协作会成为下一个"足够好"的判定标准。这个赌局的结果,可能要到v1.0之后才能看清。

你现在用哪个框架处理分布式事务?手写还是封装?如果有个装饰器能省掉80%的样板代码,你愿意为剩下的20%灵活性买单吗?