Nestjs轻量级通用存储库
#网络开发人员 #开源 #nestjs #mongodb

在本文中,我将解释如何使用Nestjs和MongoDB开发示例应用程序,并利用koude0提供的功能,这是一个轻巧和类型的安全库来无缝创建Node.js Applications的自定义数据库存储库。

示例应用程序是一家书籍经理,其中包含诸如创建,更新,删除和查找书籍之类的功能。我还将提供用于安装和运行应用程序的分步说明。

对于那些渴望获得示例应用程序的代码的人,您可以在this Github repository上找到它。您可能还可以在this other articlenode-abstract-repository库中找到进一步的解释。

域模型

应用程序域模型非常简单:Book是一个指定两个子类别的超级类型,即PaperBookAudioBook。这是其定义:

export class Book implements Entity {
  readonly id?: string;
  readonly title: string;
  readonly description: string;

  constructor(book: {
    id?: string;
    title: string;
    description: string;
  }) {
    this.id = book.id;
    this.title = book.title;
    this.description = book.description;
  }
}

export class PaperBook extends Book {
  readonly edition: number;

  constructor(paperBook: {
    id?: string;
    title: string;
    description: string;
    edition: number;
  }) {
    super(paperBook);
    this.edition = paperBook.edition;
  }
}

export class AudioBook extends Book {
  readonly hostingPlatforms: string[];

  constructor(audioBook: {
    id?: string;
    title: string;
    description: string;
    hostingPlatforms: string[];
  }) {
    super(audioBook);
    this.hostingPlatforms = audioBook.hostingPlatforms;
  }
}

koude5是一个创建的接口,旨在帮助开发人员实施类型安全域模型。它指定了所有Book或子类实例必须包含的id字段。这是因为假定id是任何存储的书的主要钥匙。但是,如果您不愿意,则不需要实现Entity;只需确保您的超类在其定义中包含一个id字段即可。

存储库

在这里,用于书籍的自定义存储库的简单定义:

@Injectable()
export class MongooseBookRepository
  extends MongooseRepository<Book>
  implements Repository<Book>
{
  constructor(@InjectConnection() connection: Connection) {
    super(
      {
        Default: { type: Book, schema: BookSchema },
        PaperBook: { type: PaperBook, schema: PaperBookSchema },
        AudioBook: { type: AudioBook, schema: AudioBookSchema },
      },
      connection,
    );
  }
}

koude11是一个打字稿通用接口,指定了一些可以在任何持久域对象上执行的常见CRUD数据库操作。另一方面,koude12是基于猫鼬的接口实现,可为您提供执行这些操作所需的所有样板代码。您可以在此article中找到有关RepositoryMongooseRepository的更多详细信息。我个人建议您阅读部分 ,以获取有关您的自定义存储库构造函数的逻辑所需的一些知识,特别是如果您的域模型为 polymormordoric ,即如果要将类型和/或其子类型的实例存储在同一MongoDB集合中。

您可能还会有一些有关InjectableInjectConnection装饰器的其他问题。请耐心等待,我将在本文中足够对他们进行ð。

控制器

控制器是对书管理员应用程序的外部请求的入口点。以下是:

type PartialBook = { id: string } & Partial<Book>;

function deserialise<T extends Book>(plainBook: any): T {
  let book = null;
  if (plainBook.edition) {
    book = new PaperBook(plainBook);
  } else if (plainBook.hostingPlatforms) {
    book = new AudioBook(plainBook);
  } else {
    book = new Book(plainBook);
  }
  return book;
}

@Controller('books')
export class BookController {
  constructor(
    @Inject('BOOK_REPOSITORY')
    private readonly bookRepository: Repository<Book>,
  ) {}

  @Get()
  async findAll(): Promise<Book[]> {
    return this.bookRepository.findAll();
  }

  @Post()
  async insert(
    @Body({
      transform: (plainBook) => deserialise(plainBook),
    })
    book: Book,
  ): Promise<Book> {
    return this.save(book);
  }

  @Patch()
  async update(
    @Body()
    book: PartialBook,
  ): Promise<Book> {
    return this.save(book);
  }

  @Delete(':id')
  async deleteById(@Param('id') id: string): Promise<boolean> {
    return this.bookRepository.deleteById(id);
  }

  private async save(book: Book | PartialBook): Promise<Book> {
    try {
      return await this.bookRepository.save(book);
    } catch (error) {
      throw new BadRequestException(error);
    }
  }
}

您可能会在这里争论几件事。对于初学者,您可能会认为企业应用程序应将业务/域逻辑委托给Domain-Driven Design (tactical design)中所述的服务对象层。我决定不出于简单目的而这样做。本文介绍的书经理是一个非常简单的CRUD应用程序,引入服务将过于工程。我宁愿实施为了最大化文章的实际目的所需的最低代码:说明如何在基于Nodejs的企业应用程序上集成koude0库。

此外,您可能不会编写deserialise功能来在处理POST请求时将JSON请求物体转换为域对象。取而代之的是,您宁愿使用NestJS pipe进行此操作,从而正确遵守单一责任原则。再次,我想分享最简单的工作示例,而牺牲了不传达Nestjs应用程序构建中建议的做法。话虽这么说,我强烈建议您阅读this section有关如何使用class-validatorclass-transformer进行JSON请求机构在开发复杂企业应用程序时的验证和挑战。

的开发中。

好吧!因此,现在缺少的一切都是如何告诉控制器在运行时使用自定义书籍存储库。这是我们接下来要去的地方。

应用程序模块

Nestjs实现了依赖性反转原理;开发人员指定其组件依赖性,Nestjs使用其内置依赖喷油器在组件实例化过程中注入这些依赖项。

那么,我们如何指定构成书本经理示例应用程序的组件的依赖项?我们需要采取两个简单的步骤:第一步包括在MongooseBookRepositoryBookController类中编写一些装饰器,就像我在代码定义中已经做过的那样。前类规定其实例是其他组件的Injectable。它还指定要实例化书籍存储库,Nestjs需要注入杂种连接。这是通过与connection构造函数输入参数相关的InjectConnection装饰器来完成的。

另一方面,BookController的定义指定,在实例化过程中,控制器消耗了由BOOK_REPOSITORY自定义代币定义的Repository<Book>的实例。

自定义令牌的定义是第二步的一部分:编写最后必需的类:AppModule。该类的定义如下:

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost:27016/book-repository'),
  ],
  providers: [
    {
      provide: 'BOOK_REPOSITORY',
      useClass: MongooseBookRepository,
    },
  ],
  controllers: [BookController],
})
export class AppModule {}

此类的Module装饰器指定在imports属性上实例化MongooseBookRepository所需的猫鼬连接。它还确定与BOOK_REPOSITORY自定义令牌标识的provider具有依赖关系的任何组件都是获得MongooseBookRepository的实例。最后,它确定BookController是书本经理应用程序的唯一控制器。

运行应用程序

一旦您自己实施了书本经理或克隆了node-abstract-repository GitHub project,并且假设您在本地计算机中安装了Nestjs,那么在运行该应用程序之前,还有两个可以满足的先决条件。

您必须安装package.json文件中指定的所有项目依赖项。为此,只需从项目的根文件夹中执行yarn install命令即可。如果您已经克隆了库项目,则该文件夹是examples/nestjs-mongoose-book-manager

第二个先决条件是安装Docker桌面(如果您尚未完成)并在本地计算机中启动它。我写了一个koude42 file,以创建一个带有最新MongoDB版本实例的Docker容器,但您可以否则可以创建自己的。

然后,您需要做的就是运行yarn start:dev。然后,您将能够通过例如curl命令或Postman访问BookController上指定的所有端点。

结论

遵循建立一个简单的Web应用程序所需的几个步骤来集成koude0库,我希望您现在能够利用自己的知识并建立自己的知识。如果您愿意创建一个复杂的应用程序,我也希望我能够为您提供一些指导。否则,请给我发表评论,我会尽快与您联系。

Michael SumUnsplash上的封面图像。