利用PostgreSQL的功能来验证数据
#postgres #database #cleancode #mongodb

来自MongoDB世界,在我开始专业使用它之前,我对SQL的熟悉程度有限。作为一名学生,我有一些基本的想法,但是请相信我,这几乎没什么好说的!

当我回头看我过去写的与操作相关的代码时,我的内心自我嘲笑我。我希望我可以去过去并重写这些代码。

我和typeorm一起进入了SQL世界。作为经验丰富的MongoDB用户,我创建的方法类似于NOSQL功能。

让我们进入一个例子。让我们提供有关我们需要实施的内容的完整规范。

  • 我们必须创建一个用户。 input payload包含电子邮件和角色ID。
  • 我们需要确保用户的电子邮件是唯一的。对于已经分配的电子邮件,我们需要提出冲突例外。
  • 我们需要确保请求者在尝试创建具有无效角色ID的用户时会收到无法处理的实体异常。为此,我们需要假设系统中的角色创建功能已在系统中可用。

非常简单,哈?

顺便说一句,什么是ORM(和Typeorm)?如果我介绍这些帖子的范围将增加。我建议检查What is an ORM by Ihechikara Vincent Abba和Typeorm的official documentation

TypeOMM提供了一种简单的方法来快速创建数据模型,使我们能够在短短几分钟内创建表和数据库约束。

长话短说,我使用Nest JS框架创建了一个tiny project,它按照Typeorm方法设计了数据模型,并构建了RoleUser表。 User表中的roleId列是Role表的外键。此外,我使email独特。

ER diagram of the DB along with primary, foreign keys of tables

屏幕截图:DB的er图以及表的主要和外键

一切都设置了。现在,这就是创建接受输入有效载荷并将用户持续到DB的方法。

我的新手自我曾经像下面的那个编写代码一样,曾经假设我需要实现的一切,我需要自己做。

async saveUserV1(userSaveRequest: UserSaveRequest): Promise<string> {
    await this.validateRole(userSaveRequest.roleId);
    await this.validateEmail(userSaveRequest.email);

    const insertResult = await this.userRepository.insert({
        email: userSaveRequest.email,
        role: {
            id: userSaveRequest.roleId,
        },
    });

    return insertResult.generatedMaps[0].id;
}

private async validateRole(roleId: string): Promise<void> {
   const isRoleExist = await this.roleRepository.exist({ where: { id: roleId } });
   if (!isRoleExist) {
       throw new UnprocessableEntityException
(`Invalid role ID`);
   }
}


private async validateEmail(email: string): Promise<void> {
   const isUserWithEmailExist = await this.userRepository.exist({ where: { email: email } });
   if (isUserWithEmailExist) {
       throw new ConflictException(`Email is associated with another user`);
   }
}

让我们审查代码。这是我的观察。

在SQL世界中,PostgreSQL之类的数据库自动处理验证。借助电子邮件字段上的唯一约束UQ_User_email以及外国密钥FK_User_roleId_Role_id强制执行的参考完整性,因此无需其他查询来验证数据。这些内置功能使手动验证冗余并减少到数据库中。

因此,作为拉的请求审稿人,我目前的自我将标记为PR的请求更改,并建议将验证责任委派给数据库。

好吧,我们需要重构代码。

我将利用数据库的内置错误处理功能。我的目标是处理异常,以便请求者获取正确的错误消息。

这是我更改的代码。

async saveUserV2(userSaveRequest: UserSaveRequest): Promise<string> {
    try {
        const insertResult = await this.userRepository.insert({
            email: userSaveRequest.email,
            role: {
                id: userSaveRequest.roleId,
            },
        });
        return insertResult.generatedMaps[0].id;
    } catch (error) {
        this.handleRepositoryException(error);
        throw error;
    }
}

private handleRepositoryException(repositoryException: RepositoryException): void {
   const errorMap: Record<string, Error> = {
       UQ_User_email: new ConflictException(`Email is associated with another user`),
       FK_User_roleId_Role_id: new UnprocessableEntityException
(`Invalid role ID`),
   };
   throw errorMap[repositoryException.constraint];
}

非常有光泽,不是吗?不幸的是,我一直在使用SQL长时间手动验证所有内容的第一种方法。

顺便说一句,所有代码均在GitHub repository上可用。欢迎您克隆它并在本地运行。

总而言之,这种思维方式的转变使我能够拥抱SQL的力量并充分利用其功能,减少执行时间并简化代码。如果数据库可以为我们处理任务,为什么不完全依靠它?

感谢您阅读它。我希望你喜欢它。我想与所有人分享我的小型Typeorm和PostgreSQL经验;这是我的第一次尝试。

nb :我从here中拍摄了封面图像。