Repositórios TypeORM

Implementação concreta de repositórios estendendo RepositoryBase.

Repositórios concretos ficam em src/infra/repositories e implementam os contratos abstratos definidos no domínio.

typescript
@Injectable()
export class PersonRepository
  extends RepositoryBase<Person>
  implements IPersonRepository
{
  constructor(@Inject(DATA_SOURCE_PROVIDER_TOKEN) dataSource: DataSource) {
    super(dataSource, Person);
  }

  findMany(query: PersonQueryDto): Promise<ListResponse<Person>> {
    const where: FindOptionsWhere<Person> = {};

    if (query.name) {
      where.name = Like(`%${query.name}%`);
    }

    if (query.active !== undefined) {
      where.active = query.active;
    }

    return this.repository
      .findAndCount({
        where,
        order: query.toFindOptionsOrder(),
        skip: query.skip(),
        take: query.limit,
      })
      .then(([items, count]) => ({
        items: this.normalizeEntities(items),
        count,
      }));
  }

  findById(id: number): Promise<Person | null> {
    return this.findOneNormalized({
      where: { id },
      relations: { address: true, contacts: true },
    });
  }
}

Operações comuns (save, delete) já estão na classe base:

typescript
export class RepositoryBase<T extends ObjectLiteral> {
  protected readonly repository: Repository<T>;

  constructor(
    protected readonly dataSource: DataSource,
    protected readonly entity: EntityTarget<T>,
  ) {
    this.repository = this.dataSource.getRepository<T>(entity);
  }

  save(entity: T) {
    return this.repository.save(entity);
  }

  async delete(entity: T) {
    await this.repository.remove(entity);
  }
}

Métodos específicos (findMany, findById, etc.) são implementados na classe concreta.

typescript
@Module({
  imports: [DatabaseModule],
  providers: [{ provide: IPersonRepository, useClass: PersonRepository }],
  exports: [DatabaseModule, IPersonRepository],
})
export class RepositoryModule {}

Graças a invalidWhereValuesBehavior.undefined: 'ignore' no DataSource, propriedades undefined em where são ignoradas pelo TypeORM — útil para filtros opcionais como name:

typescript
where: { name: query.name ? Like(`%${query.name}%`) : undefined }
  1. Estenda RepositoryBase<SuaEntidade>.
  2. Implemente a classe abstrata do domínio.
  3. Injete DATA_SOURCE_PROVIDER_TOKEN no construtor.
  4. Registre o provider no RepositoryModule.

Além dos repositórios, o InfraModule exporta serviços compartilhados:

Contrato Uso
ICacheService Cache de dados (Redis ou memória)
IRedLockService Lock distribuído de CronJobs
ILoggingService Relato de erros no ErrorsFilter

Guia de cache: Cache (Redis).