比 nestjs 更优雅的 ts 控制反转策略-依赖查找 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhennann
V2EX    Node.js

比 nestjs 更优雅的 ts 控制反转策略-依赖查找

  •  
  •   zhennann 2024-04-05 23:39:12 +08:00 2262 次点击
    这是一个创建于 552 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一、Cabloy5.0 内测预告

    Cabloy5.0 采用 TS 对整个全栈框架进行了脱胎换骨般的大重构,并且提供了更加优雅的 ts 控制反转策略,让我们的业务开发更加快捷顺畅

    1. 新旧技术栈对比:

    后端 前端
    旧版 js 、egg2.0 、mysql js 、vue2 、framework7
    新版 ts 、egg3.0 、多数据库兼容(支持 mysql 、postgresql ) ts 、vue3 、quasar

    2. 框架开发两大趋势

    1. TS 化:这是显而易见的趋势,不必赘言

    2. ESM 化:从目前趋势看,前端框架已经全链路 ESM 化了,但是,大多数后端框架仍然是 Commonjs 。即便是 egg3.0 也仍然是 Commonjs 。由于 egg 的定位仍然是元框架,CabloyJS5.0 在 egg 基础上仍然开发出了全量 ESM 化的业务模块化系统(在 Commonjs 之上进行 ESM 化的具体机制是什么,另有文章阐述)

    二、比 nestjs 更优雅的 ts 控制反转策略

    基于 TS 的后端框架一般都会提供依赖容器,实现控制反转。控制反转有两种策略:依赖注入依赖查找。CabloyJS5.0 同时支持依赖注入依赖查找,并且通过模块范围的辅助,让依赖查找的代码更加简洁高效,下面挑几个特性举例说明:

    1. Service 服务
    2. Config 配置
    3. 多语言
    4. 错误异常

    三、Service 服务

    1. 创建一个 Service

    在 CabloyJS 中,local bean 相当于 nestjs 中 service 的概念,下面创建一个 local bean

    import { BeanBase, Local } from '@cabloy/core'; import { ScopeModule } from '../resource/this.js'; @Local() export class LocalHome extends BeanBase<ScopeModule> { async echo({ user: _user }) { return `Hello World!`; } } 
    1. 通过@Local声明 LocalHome 是一个 local bean

    2. LocalHome 继承自基类 BeanBase

    2. Service 的依赖注入

    接下来,在 Controller 中采用依赖注入的方式来使用 LocalHome

    import { BeanBase, Controller, Use } from '@cabloy/core'; import { ScopeModule } from '../resource/this.js'; import { LocalHome } from '../local/home.js'; @Controller() export class ControllerHome extends BeanBase<ScopeModule> { @Use() home: LocalHome; async echo() { const res = await this.home.echo({ user: this.ctx.state.user.op, }); this.ctx.success(res); } } 
    1. 使用@Use注入 LocalHome

    3. Service 的依赖查找

    接下来,在 Controller 中采用依赖查找的方式来使用 LocalHome

    import { BeanBase, Controller } from '@cabloy/core'; import { ScopeModule } from '../resource/this.js'; @Controller() export class ControllerHome extends BeanBase<ScopeModule> { async echo() { const res = await this.scope.local.home.echo({ user: this.ctx.state.user.op, }); this.ctx.success(res); } } 
    1. 不需要导入 LocalHome ,直接在代码中使用this.scope.local来访问容器中的 local bean 实例

    看一下动画演示,提供了完整的类型智能提示:

    lookup-config.gif

    四、Config 配置

    1. 定义 Config

    可以为业务模块单独定义一些 Config 配置,如下:

    import { CabloyApplication } from '@cabloy/core'; export const cOnfig= (_app: CabloyApplication) => { return { + prompt: 'hello world', }; }; 

    2. 使用 Config

    可以在 LocalHome 中直接使用刚才定义的 config

    import { BeanBase, Local } from '@cabloy/core'; import { ScopeModule } from '../resource/this.js'; @Local() export class LocalHome extends BeanBase<ScopeModule> { async echo({ user: _user }) { + return this.scope.config.prompt; - return `Hello World!`; } } 
    1. 不需要导入任何类型,直接在代码中使用this.scope.config来访问当前业务模块中的 config 配置

    看一下动画演示,提供了完整的类型智能提示:

    lookup-config.gif

    五、多语言

    1. 定义语言资源

    可以为业务模块定义语言资源,比如,这里分别定义英文和中文两种语言资源

    英文

    export default { HelloWorld: 'Hello World', }; 

    中文

    export default { HelloWorld: '您好世界', }; 

    2. 使用语言资源

    可以在 LocalHome 中直接使用刚才定义的语言资源

    import { BeanBase, Local } from '@cabloy/core'; import { ScopeModule } from '../resource/this.js'; @Local() export class LocalHome extends BeanBase<ScopeModule> { async action({ user: _user }) { + // 自动判断当前语言 + const message = this.scope.locale.HelloWorld(); + // 强制使用英文资源 + const message1 = this.scope.locale.HelloWorld.locale('en-us'); + // 强制使用中文资源 + const message2 = this.scope.locale.HelloWorld.locale('zh-cn'); + return `${message}:${message1}:${message2}`; - return this.scope.config.prompt; } } 
    1. 不需要导入任何类型,直接在代码中使用this.scope.locale来访问当前业务模块中的语言资源

    看一下动画演示,提供了完整的类型智能提示:

    lookup-locale.gif

    六、错误异常

    1. 定义错误码

    可以为业务模块定义错误码

    export enum Errors { + Error001 = 1001, } 
    1. 这里定义了一个错误枚举类型 Error001 ,对应的错误码是 1001

    2. 定义错误码对应的语言资源

    可以为错误码定义语言资源,比如,这里分别定义英文和中文两种语言资源

    英文

    export default { + Error001: 'This is a test', HelloWorld: 'Hello World', }; 

    中文

    export default { + Error001: '这是一个错误', HelloWorld: 'Hello World', }; 

    3. 抛出错误异常

    可以在 LocalHome 中直接使用刚才定义的错误枚举值,并抛出异常

    import { ScopeModule } from '../resorce/this.js'; @Local() export class LocalHome extends BeanBase<ScopeModule> { async action({ user: _user }) { + // 直接抛出异常 + this.scope.error.Error001.throw(); - return this.scope.config.prompt; } } 
    1. 不需要导入任何类型,直接在代码中使用this.scope.error来访问当前业务模块中的错误枚举值

    看一下动画演示,提供了完整的类型智能提示:

    lookup-error.gif

    六、后记

    Cabloy4.0 中就已经提供了大量业务能力,比如:工作流引擎、表单引擎、权限引擎、字段权限、多级缓存、模块化体系、分布式架构、多租户引擎,等等。随着 Cabloy5.0 Typescript 的赋能,这些业务能力也随之有了全新的表现

    欲了解更多,请关注每晚 8 点 B 站直播:濮水代码

    7 条回复    2024-04-07 12:36:11 +08:00
    isbase
        1
    isbase  
       2024-04-05 23:55:03 +08:00
    看起来用法没有 tegg 简洁
    zachwei
        2
    zachwei  
       2024-04-06 08:35:45 +08:00
    好像 Java
    iPhone15
        3
    iPhone15  
       2024-04-06 13:01:54 +08:00 via iPhone   2
    对这些概念完全不感冒,我信仰 FP
    uni
        4
    uni  
       2024-04-06 13:25:03 +08:00
    @iPhone15 大佬信仰 fp 的用哪个后端框架最好?
    mmdsun
        5
    mmdsun  
       2024-04-06 21:52:07 +08:00 via iPhone   1
    @uni 后端 FP 的话看看 Vert.X 、Spring Webflux ?不过不支持 js 就是了,Java 、kotlin 、Groovy 语言
    uni
        6
    uni  
       2024-04-06 23:36:18 +08:00
    @mmdsun 感谢,我看一下。我现在的选择是 f#……
    newghost
        7
    newghost  
       2024-04-07 12:36:11 +08:00
    看 react hooks 去继承化是趋势,使用继承会导致 JS 的内存对象原型链依赖过长,不利于内存的快速回收,加大内存泄露的风险
    都用上装饰器了,像 extend BeanBase<ScopeModule> 其实都可以写进装饰器里。否则就太像 JAVA 了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3380 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 04:30 PVG 12:30 LAX 21:30 JFK 00:30
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86