继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

让我们认真聊聊Node.js中的依赖注入

临摹微笑
关注TA
已关注
手记 331
粉丝 32
获赞 170
一旦你尝到了这种风味,你的 Node 应用将会彻底改变。

照片由Natalie Runnerstrom拍摄,来自Unsplash

嘿,你!对,就是你在听……你看起来像一个JavaScript开发者,对吧?充满激情和动力——你甚至阅读Medium上的Node文章。恭喜,这是迈向更好世界的良好开端。

我又有一个问题:你的Node应用中的依赖注入是怎么实现的?你有没有想过这个问题?

你是不是那种仅仅依赖 ECMAScript 模块来在整个应用程序中导入和导出函数及逻辑的开发者?随意地通过永远连接的函数的七个(或更多 😔)参数传递数据?通过六层抽象传递同样的信息?通过函数参数传递同样的信息?最终得到一个混乱的混合了函数式和其他未知架构模式的应用程序——这是 JavaScript 中非常常见的场景?

如果是的话,放心吧:你并不孤单。然而,这并不意味着不应该改变……

你没有理由再找借口了:借助 TypeScript,你可以开发出美观且面向对象的应用程序,这些应用可以依赖于适当的依赖注入。一旦你体验过这种开发方式,就再也回不到那些意大利面条般的代码了:我保证。除非它们看起来和那些一样。

照片由 Sofia CiravegnaUnsplash 拍摄

会带来什么呢?

  • 你将能够依赖面向对象编程的强大特性,并从中受益于封装抽象多态继承
  • 你将能够应用SOLID原则,显著提升可扩展性可测试性可读性可靠性
  • 你将能够控制应用程序启动时的异步任务。

让我们来探索一种实现合适的面向对象架构的方法,在Node中。不需要重新发明轮子——因此我们可以采用一种经典的方法(如果你熟悉Java,应该会感到很熟悉)。

这种方法基于以下概念如下。

控制层,服务层,仓储层模式

三层对应三个问题:

  • 控制器:管理 输入输出(I/O) 并确保应用程序 框架(如 REST、Kafka)与业务逻辑之间的连接。
  • 服务:包含应用程序的 业务逻辑,即业务 规则政策和策略
  • 仓库和客户端:整合 外部依赖项,如第三方 API。

可以在这里了解更多 👉 这里

单例模式(一种设计模式)

仓库、服务和控制器是应该只实例化一次的类。实例化工作由一个名为 getControllers() 的函数完成,该函数负责返回应用程序中的所有控制器。

仓库和客户端程序首先实例化,然后被注入到服务中,这些服务接着被注入到控制器中——就像套娃那样。

这种方法让你能够完全掌控应用中资源创建的过程。你可以轻松处理创建控制器时所需的同步和异步操作。

一个控制器对应一个路由

控制器然后通过路由器映射到API接口路由。每个控制器只处理一个路由请求。

好的,我们来看看用这种方法实际的服务是什么样的。

我们打算添加一个新的端点,负责返回某个客户的结构化信息。数据来自不同的来源渠道。

  • 后端应用程序的数据库,存储了客户数据。
  • 一个第三方产品API
  • 一个提供营销数据的第三方API

以下是实现GET /customer/:id/info端点所需的不同类。

我们可以做到

    import { Customer, CustomerRegistry } from './registries/CustomerRegistry'  
    import { ProductCatalogClient } from './clients/ProductCatalogClient'  
    import { Logger } from './Logger'  
    import { MerchandisingProfile } from './types'  
    import { MerchandisingClient } from './clients/MerchandisingClient'  

    export class CustomerInfo {  
      readonly customerRegistry: CustomerRegistry  
      readonly productsClient: ProductCatalogClient  
      readonly merchandisingClient: MerchandisingClient  
      readonly formatter: InfoFormatter  
      readonly logger: Logger  

      constructor(  
        customerRegistry: CustomerRegistry,  
        productsClient: ProductCatalogClient,  
        merchandisingClient: MerchandisingClient,  
        formatter: InfoFormatter,  
        logger: Logger,  
      ) {  
        this.customerRegistry = customerRegistry  
        this.productsClient = productsClient  
        this.merchandisingClient = merchandisingClient  
        this.formatter = formatter  
        this.logger = logger  
      }  

      public async getCustomerInfo(id: string): Promise<string> {  
        const customer = await this.customerRegistry.getCustomerById(id)  
        const { email } = customer  

        const products = await this.productsClient.getCustomerProducts(email)  
        const profile = await this.getMerchandisingProfile(customer)  

        return this.formatter.formatCustomerInfo(customer, products, profile)  
      }  

      private async getMerchandisingProfile({  
        id,  
        email,  
        fullname,  
      }: Customer): Promise<MerchandisingProfile | undefined> {  
        try {  
          return await this.merchandisingClient.getUserProfile(fullname, email)  
        } catch (e) {  
          this.logger.warn(`无法获取ID为 ${id} 的用户营销资料`)  
        }  
      }  
    }

哇哦!这看起来好整洁🤩!你有什么秘诀吗?

  • 只有类型是通过模块导入的唯一实体。
  • 类的 依赖 通过 构造函数参数 传递,并绑定到 类成员,因此它们在运行时可以直接使用于所有方法中。
  • 方法 签名 保持 简明,因为依赖不需要作为参数传递,因此参数则专门用于与给定请求相关的特定上下文(例如,我们示例中的消费者 ID)。

就这样!另一个让你的同事们对你刮目相看并爱上你代码的妙招。
通过函数参数进行不一致的依赖注入这种事儿将一去不复返。
希望这篇文章你看了觉得还行,随时欢迎你分享你的想法,我们再深入聊聊。

别忘了:保持代码整洁 🧹。

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP