如何在 NestJs 服务中组织抛出业务逻辑异常?

Niv*_*lis 8 javascript architecture node.js typescript nestjs

我对这个github问题讨论有一些疑问:https : //github.com/nestjs/nest/issues/310

我想按照shekohex建议从我的服务方法中抛出业务域异常。我想知道我应该把它们放在哪里?我相信它们中的许多跨域(例如EntityNotFoundExceptionActionForbiddenException等)将非常相似,因此将它们保留在应用程序级别(某种共享模块)是有意义的。另一方面,这会使特定域的独立性降低(例如,如果我需要在将来某个时候将其中的很少一部分提取到另一个应用程序中怎么办?)。更重要的是,一些例外可能是特定于域的,我必须将它们保留在适当的域模块结构中。

假设我确实将其中一些保留在共享模块中,而将其余部分保留在各个域目录中。我如何将它们映射到正确的HttpExceptions?如果我进行全局异常过滤,我也会使我的域控制器更加依赖于应用层。如何映射特定于域的异常?我是否创建另一个模块级异常过滤器?

创建全局或模块级过滤器是一种方法吗?装饰每个端点UseFilters似乎很麻烦。

预先感谢您的意见!

A. *_*tre 7

这是我最近一直在处理的一个很好的问题。

对于某些项目,我们希望在应用程序的不同域之间共享业务错误。我们最终得到的只是一个共享(或公共)文件夹或模块,用于暴露我们在应用程序中遇到的不同域特定错误。

最重要的是,我们使用 Nest 的常见例外,这些例外非常适合这项工作!

最后,我们使用我们共享的域特定业务错误自定义常见的 Nest 异常。

极简user.service.ts再现示例:

import { NotFoundException, Logger } from '@nestjs/common';
import { UserBusinessErrors } from '../../shared/errors/user/user.business-errors';

// some code ...

// Find the users from the usersIds array
const dbUsers = await this.userRepository.find({ id: In(usersIds) });
// If none was found, raise an error
if (!dbUsers) {
    Logger.error(`Could not find user with IDs: ${usersIds}`, '', 'UserService', true);
    throw new NotFoundException(UserBusinessErrors.NotFound);
}
Run Code Online (Sandbox Code Playgroud)

及其user.business-errors.ts在共享或公共模块/文件夹中的相应文件(例如src/shared/errors/user/user.business-errors.ts):

export const UserBusinessErrors = {
    // ... other errors

    NotFound: {
        apiErrorCode: 'E_0002_0002',
        errorMessage: 'User not found',
        reason: `Provided user ids doesn't exist in DB`
    },

    // ... other errors
}
Run Code Online (Sandbox Code Playgroud)

或者有时如果你想要更通用的东西,你也可以使用共享错误:

import { InternalServerErrorException, Logger } from '@nestjs/common';
import { SharedBusinessErrors } from '../../shared/errors/shared.business-errors';

// some code ...

try {
    // ...
} catch (error) {
    Logger.log(SharedBusinessErrors.DbSaveError, 'UserService');
    throw new InternalServerErrorException(SharedBusinessErrors.DbSaveError, error.stack);
}
Run Code Online (Sandbox Code Playgroud)

与相应的shared.business-errors.ts文件:

export const SharedBusinessErrors = {
    DbSaveError: {
        apiErrorCode: 'E_0001_0001',
        errorMessage: `Error when trying to save resource into the DB`,
        reason: 'Internal server error'
    },

    // ... other errors
}
Run Code Online (Sandbox Code Playgroud)

对您的不同问题发表评论的最后想法:

  • 正如你所说

创建全局或模块级过滤器是一种方法吗?用 UseFilters 装饰每个端点似乎很麻烦。

  • 我们可以利用Nest 过滤器自定义并在控制器或路由级别使用装饰器。但对我来说,您实际上仍然需要指定与它相关的业务错误,以便为业务错误增加价值,并且无论如何您都需要在服务中引发错误。因此,只有一些包含域特定(或业务)错误和更通用的共享文件就可以了(但可能不是最干净/最优雅的解决方案)。

  • 您可以将您的业务错误文件放在每个模块的文件夹中,并添加一个 webpack 配置来收集所有这些文件并将其连接到一个唯一的文件中。

最后,我想如果您需要跨多个项目的特定域或共享错误,您甚至可以创建一个 npm 包。

如果我对任何事情不清楚或/以及它是否能让您有更多的了解,请告诉我!