首页 > 解决方案 > 如何正确实施存储库适配器?

问题描述

我开始学习如何基于 TypeScript 和 NestJS 构建 Clean Architecture。在我开始实现存储库适配器和控制器之前,一切都很好。主要问题是 API 方法和用例的返回类型不兼容。

想法是将实体和用例放在核心文件夹中,其中用例使用存储库适配器(通过 DI)。此适配器也从核心文件夹实现存储库接口。

存储库适配器的实现包含在app中。app还包含 NestJS 实现、TypeOrm 实体等。但我也想为一些控制器使用存储库,例如getAll query.

!!!和问题!!!

对于几乎每个命令,我都必须使用Mappers,因为 TypeORM 实体和域实体是不兼容的类型。我认为如果我们将数据传递给用例就可以了,因为我们需要将数据从 TypeOrm 形状转换为域形状。

但是如果我只是在控制器中调用存储库适配器方法,我需要再次映射数据......而且我不知道如何跳过不必要的步骤。在我的想象中,我可以在应用服务中调用存储库方法,就是这样。如果我跳过映射,那么所有数据属性都将具有前缀_(

也许有人遇到了同样的问题?

//核心区

账户实体 ( /domain/account):

export type AccountId = string;
export class Account {
constructor(
  private readonly _id: AccountId,
  private readonly _firstName: string
) {}

  get id(): AccountId {
   return this._id;
  }

  get firstName() {
   return this._firstName;
  }
}

存储库接口(repositories/account-repository):

import { Account } from '../domains/account';

export interface AccountRepository {
  getAccountById(id: string): Promise<Account>;
  getAllAccounts(): Promise<Account[]>;
}

具有存储库的用例示例:

import { AccountRepository } from '../../repositories/account-repository';

export class ToDoSomething {
  constructor(private readonly _accountRepository: AccountRepository) {}

  async doSomethingWithAccount(command): Promise<boolean> {
    const account = await this._accountRepository.getAccountById(
      command.accountId,
    );

    if (!account) {
      return false;
    }

    return true;
  }
}

//应用专区

存储库适配器:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Account } from '../../../../core/domains/account';
import { AccountRepository } from '../../../../core/repositories/account-repository';
import { AccountEntity } from '../account.entity';
import { AccountMapper } from '../account.mapper';

@Injectable()
export class AccountRepositoryAdapter implements AccountRepository {
  constructor(
    @InjectRepository(AccountEntity)
    private readonly _accountRepository: Repository<AccountEntity>,
  ) {}

  async getAccountById(id: string): Promise<Account> {
    return this._accountRepository.findOne({ id: id });
    // will return { id: 1, firstName: "name" }
    // and because I need to use MapToDomain
  }

  async getAllAccounts(): Promise<Account[]> {
    return this._accountRepository.find();
    // Here too I need to use MapToDomain for every item
  }
}

TypeOrm 帐户:

import {
  Column,
  Entity,
  PrimaryGeneratedColumn,
} from 'typeorm';

@Entity({ name: 'account' })
export class AccountEntity {
  @PrimaryGeneratedColumn()
  id: string;

  @Column()
  firstName: string;
}

标签: typescriptbackendnestjstypeormclean-architecture

解决方案


在 Clean Architecture 中,控制和数据流通常是这样的:控制器从视图(例如 Web 应用程序)获取请求并将其转换为请求模型,然后传递给用例。用例从请求模型中读取它应该计算的内容并使用存储库与域实体交互以最终创建响应模型。然后将响应模型传递给演示者(可能与控制器属于同一类),后者将其转换为视图的响应。

控制器通常不与域实体甚至 ORM 类型交互。

查看我关于实施清洁架构的博客系列以获取更多详细信息:http ://www.plainionist.net/Implementing-Clean-Architecture-Controller-Presenter/


推荐阅读