V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhennann
V2EX  ›  Node.js

使用 ts 的最佳境界:化类型于无形

  •  
  •   zhennann · 19 天前 · 1497 次点击

    使用 ts 的最佳境界:化类型于无形

    在项目中使用 ts 可以带来类型智能提示与校验的诸多好处。同时,为了减少类型标注,达到化类型于无形的效果,CabloyJS 引入了 ioc 和依赖查找的机制。在上一篇文章中,我们创建了一个业务模块 test-home ,并且采用依赖查找的机制演示了如何优雅的定义和使用资源,包括:Service 服务、Config 配置、国际化语言资源、Error 错误异常

    在实际的项目当中,经常会遇到跨模块访问资源的场景,那么,CabloyJS 的依赖查找机制是否仍然可以优雅的实现跨模块访问呢?让我们一睹为快

    模块化体系与任务说明

    CabloyJS 全栈框架的前后端均采用模块化体系。一个 CabloyJS 项目由多个业务模块组成,每个业务模块都可以包含与自身业务相关的资源,比如:Service 服务、Config 配置、国际化语言资源、Error 错误异常、中间件、定时任务、消息队列、系统启动项,等等

    在这里,我们创建一个新的业务模块 test-work ,在 test-work 中访问 test-home 提供的资源

    1. 新建业务模块

    cabloy api:create:module test-work
    

    2. 新建 API

    通过一个命令同时创建一组文件:Route 、Controller 、Service

    cabloy api:create:controller work
    

    3. 跨模块访问 Service 服务

    接下来,我们在刚才新建的 Service 当中,访问模块 test-home 的 Service 服务

    import { BeanBase, Local } from '@cabloy/core';
    import { ScopeModule } from '../resource/this.js';
    
    @Local()
    export class LocalWork extends BeanBase<ScopeModule> {
      async action({ user }) {
    +   const scopeHome = this.getScope('test-home');
    +   return scopeHome.local.home.action({ user });
        // return user;
      }
    }
    
    1. 通过 getScope 方法获取模块 test-home 的 scope 对象
    2. 通过 scope 对象直接访问 Service 服务: home

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

    cross-module-localbean.gif

    4. 跨模块访问 Config 配置

    访问模块 test-home 的 Config 配置

    import { BeanBase, Local } from '@cabloy/core';
    import { ScopeModule } from '../resource/this.js';
    
    @Local()
    export class LocalWork extends BeanBase<ScopeModule> {
      async action({ user }) {
        const scopeHome = this.getScope('test-home');
    +   const prompt = scopeHome.config.prompt;
        return scopeHome.local.home.action({ user });
        // return user;
      }
    }
    
    1. 直接通过 scopeHome 取得 config 中的 prompt 属性值

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

    cross-module-config.gif

    5. 跨模块访问国际化语言资源

    访问模块 test-home 的国际化语言资源

    import { BeanBase, Local } from '@cabloy/core';
    import { ScopeModule } from '../resource/this.js';
    
    @Local()
    export class LocalWork extends BeanBase<ScopeModule> {
      async action({ user }) {
        const scopeHome = this.getScope('test-home');
    +   const message = scopeHome.locale.HelloWorld();
    +   const message1 = scopeHome.locale.HelloWorld.locale('en-us');
    +   const message2 = scopeHome.locale.HelloWorld.locale('zh-cn');
        return scopeHome.local.home.action({ user });
        // return user;
      }
    }
    

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

    cross-module-locale-small.gif

    6. 跨模块访问 Error 错误异常

    抛出模块 test-home 提供的 Error 错误异常

    import { BeanBase, Local } from '@cabloy/core';
    import { ScopeModule } from '../resource/this.js';
    
    @Local()
    export class LocalWork extends BeanBase<ScopeModule> {
      async action({ user }) {
        const scopeHome = this.getScope('test-home');
    +   scopeHome.error.Error001.throw();
        return scopeHome.local.home.action({ user });
        // return user;
      }
    }
    
    1. 直接通过 scopeHome 抛出错误异常 Error001

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

    cross-module-error.gif

    后记

    CabloyJS 采用 ioc 和依赖查找的机制,让 ts 的使用达到了化类型于无形的最佳境界,从而让我们的代码保持优雅和简洁,进而也能显著提升开发效率,保证代码质量

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

    13 条回复    2024-04-12 10:19:21 +08:00
    calmbinweijin
        1
    calmbinweijin  
       19 天前
    没说清楚,但是感觉有点意思
    arfaWong
        2
    arfaWong  
       19 天前
    看标题进来还以为 anyscript 呢,哈哈哈哈
    zhennann
        3
    zhennann  
    OP
       19 天前
    @arfaWong 所以写这篇文章,必须得配动图,要不还以为又回到了 anyscript
    encro
        4
    encro  
       19 天前
    难道这个框架是我这个问题的答案? https://www.v2ex.com/t/1026896#reply7
    encro
        5
    encro  
       19 天前
    不过你这写法,让跨模块失去了意义呢。
    zhennann
        6
    zhennann  
    OP
       19 天前
    @encro 这句话不理解
    zhennann
        7
    zhennann  
    OP
       19 天前
    @encro 跨模块,不就是资源按模块隔离,然后再跨模块访问吗?
    encro
        8
    encro  
       19 天前
    @zhennann

    模块化的意义在于只通过模块的接口去访问,
    如果可以任意访问模块的方法或者数据,就失去模块化黑箱的意义了吧,没有达到解耦的目的。
    我通过例子是没看出这种模块化的意义。
    当然可能是因为 config 和 error 都属于 base 模块。
    zhennann
        9
    zhennann  
    OP
       19 天前
    @encro

    如果只通过接口访问模块的资源,更好的方案应该是微服务
    在一个大型项目中,模块的资源不只有 api 接口,config 和 error 资源也可以按照模块隔离,这样可以让与某个业务相关的资源代码充分自治
    encro
        10
    encro  
       19 天前
    @zhennann

    在一个大型项目中,模块的资源不只有 api 接口,config 和 error 资源也可以按照模块隔离,这样可以让与某个业务相关的资源代码充分自治


    是这样没错,所以这样直接访问,就又变成了不自治了,不是吗?
    zhennann
        11
    zhennann  
    OP
       19 天前
    @encro 总感觉你要的是微服务架构,而不是模块化架构
    zhennann
        12
    zhennann  
    OP
       18 天前
    @encro
    在模块化架构中也可以实现你所说的“黑箱隔离”。其他模块需要什么 config 资源,在本模块通过 Service 服务包装提供即可
    encro
        13
    encro  
       18 天前
    @zhennann

    是的,在架构上,尽量向微服务靠拢,减少模块交互。
    config ,location, error,log ,queue 这些都是属于框架级别的可以共用。
    user,auth,storage,payment 之类属于应用级别的应该尽量隔离。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1095 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 18:12 · PVG 02:12 · LAX 11:12 · JFK 14:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.