尝试 Node 搭建后端-开发框架

昨天搭建的后端只是一个简单的架子,解决的只是最简单的问题-如何启动一个Web服务以及这个服务怎么开发、测试、构建。

而在真实的(或者说Production Ready的)开发场景中,我们常常需要一些开发框架,帮我们解决代码管理上的问题。比如:代码如何保持代码结构的稳定(多实现、减小变更的影响范围等),如何请求层进行拦截(AOP)等等。

说白了,就是后端开发固有的思维模式 IOC、AOP 这些东西在 Node 里面咋做~

这里我使用的是 @fastify/awilix,这并不是注解编程的风格的框架,需要你自己主动感知(或者说是主动选择)使用IOC、DI。

IOC、DI 的使用

javascript 复制代码
// app.js
const fastify = require('fastify')
const { fastifyAwilixPlugin } = require('@fastify/awilix')

const app = fastify({ logger: true })

app.register(fastifyAwilixPlugin, {
  disposeOnClose: true,
  disposeOnResponse: true,
})

// 后续的注册和路由代码都写在这里

app.listen({ port: 3000 })
javascript 复制代码
// ---------- 1. 定义服务类 ----------
// services/UserRepository.js
class UserRepository {
  constructor({ dbClient }) {
    this.dbClient = dbClient
  }

  async findUser(id) {
    // 模拟数据库查询
    return { id, name: 'Alice', email: 'alice@example.com' }
  }
}

// services/UserService.js
class UserService {
  constructor({ userRepository, logger }) {
    this.userRepository = userRepository
    this.logger = logger
  }

  async getUser(id) {
    this.logger.info(`Fetching user: ${id}`)
    return this.userRepository.findUser(id)
  }
}

// services/Logger.js
class Logger {
  info(msg) { console.log(`[INFO] ${msg}`) }
  error(msg) { console.error(`[ERROR] ${msg}`) }
}
javascript 复制代码
// ---------- 2. 注册到 IoC 容器 ----------
const { diContainer } = require('@fastify/awilix')
const { asClass, Lifetime } = require('awilix')

// 注册所有服务(app 级别的单例)
diContainer.register({
  logger: asClass(Logger, { lifetime: Lifetime.SINGLETON }),
  dbClient: asClass(require('./services/DbClient'), { lifetime: Lifetime.SINGLETON }),
  userRepository: asClass(require('./services/UserRepository'), { lifetime: Lifetime.SINGLETON }),
  userService: asClass(require('./services/UserService'), { lifetime: Lifetime.SINGLETON }),
})
javascript 复制代码
// ---------- 3. 在路由中使用 DI ----------
app.get('/users/:id', async (request, reply) => {
  // 从容器中解析 userService(所有依赖自动注入)
  const userService = app.diContainer.resolve('userService')
  // 或简写:const { userService } = app.diContainer.cradle

  const user = await userService.getUser(request.params.id)
  return { success: true, data: user }
})

AOP的使用

Fastify 本身支持 Hooks来实现,我这里目前想到的是鉴权、日志、全局异常处理等等

javascript 复制代码
export function buildApp(opts?: { logger?: boolean }): FastifyInstance {
  const app = Fastify({
    logger: opts?.logger ?? true,
  });

  app.register(jwt, {
    secret: config.jwtSecret,
    sign: { expiresIn: config.jwtExpiresIn },
  });

  setupAuth(app);

  app.register(authRoutes);
  app.register(healthRoutes);
  app.register(supportRoutes);

  app.setErrorHandler((error, request, reply) => {
    if (error instanceof ApiError) {
      return reply.status(error.statusCode).send({
        status: 'error',
        errorCode: error.errorCode,
        errorMsg: error.message,
      });
    }

    if (error instanceof Error && 'validation' in error) {
      return reply.status(400).send({
        status: 'error',
        errorCode: 'VALIDATION_ERROR',
        errorMsg: error.message,
      });
    }

    request.log.error(error);
    return reply.status(500).send({
      status: 'error',
      errorCode: 'INTERNAL_ERROR',
      errorMsg: 'Internal server error',
    });
  });

  app.setNotFoundHandler((request, reply) => {
    return reply.status(404).send({
      status: 'error',
      errorCode: 'NOT_FOUND',
      errorMsg: `Route ${request.method} ${request.url} not found`,
    });
  });

  return app;
}

这里我最开始有个疑问,为什么没有使用注解驱动的编程规范?

后来我想了想,发现这里其实隐含了大量历史信息~

在 Java 里 Spring 最开始提倡的是配置驱动,大量的 xml 配置文件来定义bean的生命周期、依赖关系。直到后来 Spring Boot 流行起来才开始有了现在的注解驱动。

**我感觉这里编程模式其实是随着框架的副产品。**如果一个框架能流行起来不会是因为他的编程风格特别优雅,而是框架能解决特定的问题,编程风格算是作者的偏好。

相关推荐
只睡四小时11 小时前
MCP 弃用 SSE?Node 分帧漏 81.7% 事件
node.js·webassembly·实时通信·mcp·streamable http
hey you~21 小时前
个体户门店办理400电话,如何对接订单系统实现来电弹窗
node.js·webhook·400电话·门店数字化·来电弹窗·订单系统对接
YIAN2 天前
RAG 学到最后,我真正学会的不是“检索”,而是让系统知道自己缺什么
langchain·node.js·llm
用户11977079480732 天前
GitHub 电商开源项目分享:用 sharp 批量出饰品主图,试了裁剪、细节图和色彩配置文件三个坑
前端·node.js
donecoding2 天前
pnpm 配好了,为什么执行的还是另一份?命令入口与运行时排查
node.js·前端工程化
江屿风2 天前
【Plain Language Large Model】【从浏览器到全栈:一课精通Node.js三剑客】流食般投喂
笔记·前端框架·npm·node.js·js·npx
SomeOtherTime2 天前
node.js基础(AI回答)
node.js
雨季mo浅忆3 天前
记录Node.js安全漏洞及其内置Brotli解码器问题
node.js·安全性测试
tommyjiatong3 天前
资安底座连环雷:从 PHP2Shell 到 Node2Shell
node.js·php
SEO_juper3 天前
Bash 交叉验证 robots.txt 和 sitemap:两个文件自相矛盾,页面永远收录不了
开发语言·python·node.js·json·bash