【问题标题】:How to correct implement the Repository Adapter?如何正确实施存储库适配器?
【发布时间】:2021-10-05 13:43:41
【问题描述】:

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

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

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

!!!还有问题!!!

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

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

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

//核心区

帐户实体 (/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;
  }
}

存储库接口(@98​​7654326@):

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;
}

【问题讨论】:

    标签: typescript backend nestjs typeorm clean-architecture


    【解决方案1】:

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

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

    查看我关于实施清洁架构的博客系列了解更多详情:http://www.plainionist.net/Implementing-Clean-Architecture-Controller-Presenter/

    【讨论】:

    • 太棒了。我以为我正在使用数据映射做额外的步骤。但无论如何,我们应该将请求数据形成域形式。当然,需要重新形成。我们没有其他办法。你能建议为几乎每个 API 调用创建用例吗?像 GET:/get-all-users 等等。我得到这个问题的原因是。如果我们不需要做一些领域逻辑,我们应该转换列表中的每个项目吗?因为在我看来我们可以避免它,但我不知道该怎么做..(也许我们可以跳过转换为域模型并立即将数据传递给 Presenter。当然,这是正确的?
    • 根据我的经验,“捷径”在项目开始或几乎不需要响应请求的逻辑时是可以的。随着时间的推移,项目通常会变得更加复杂,然后应该删除捷径,然后真的每个请求都会成为我们的用例。
    猜你喜欢
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 1970-01-01
    • 2022-12-22
    相关资源
    最近更新 更多