CRM Docs
Arquitetura Backend

DefaultRepositoryGateway

CRUD padrão com KnexDefaultRepository e MapperGateway

Para agregados com CRUD simples em uma única tabela, o projeto padroniza um par de classes base que eliminam boilerplate.

Contrato: DefaultRepositoryGateway

Arquivo: src/app/gateways/default.repository.gateway.ts

export abstract class DefaultRepositoryGateway<
  APP,
  INFRA,
  PRIMARY_KEY extends keyof INFRA,
> {
  abstract generateNewId(companyId: number): Promise<number>;
  abstract findAll(companyId: number): Promise<APP[]>;
  abstract findById(companyId: number, id: INFRA[PRIMARY_KEY]): Promise<Nullable<APP>>;
  abstract create(companyId: number, entity: APP, selfIdGeneration?: boolean): Promise<APP>;
  abstract update(companyId: number, entity: APP): Promise<APP>;
  abstract delete(companyId: number, id: INFRA[PRIMARY_KEY]): Promise<void>;
}

Características importantes:

  • Todo método recebe companyId como primeiro parâmetro (multi-tenant).
  • APP = entity da camada application (src/app/entities/).
  • INFRA = tipo da linha do banco (src/infra/database/knex/entities/).
  • PRIMARY_KEY = nome literal da coluna PK (ex.: 'CD_FILA'), garantindo type-safety no findById e delete.

Implementação: KnexDefaultRepository

Arquivo: src/infra/database/knex/repositories/knex-default.repository.ts

A classe implementa DefaultRepositoryGateway (não estende o gateway abstrato). Ela recebe no constructor:

ParâmetroExemploFunção
companyDbGeneratorCompanyKnexProviderResolve conexão Knex por empresa
tableName'FILA'Nome da tabela MSSQL
primaryKey'CD_FILA'Coluna de chave primária
mapperQueueMapperConversão APP ↔ INFRA

Separação de métodos públicos e internos

MétodosRetornoResponsabilidade
findAll, findById, create, update, deleteAPPAplicam mapper.toEntity() / toPersistence()
_findAll, _findById, _create, etc.INFRAAcesso direto ao Knex, sem mapping

Isso permite que subclasses estendam lógica de banco reutilizando _-methods quando necessário.

MapperGateway

Arquivo: src/app/gateways/mapper.gateway.ts

export abstract class MapperGateway<APP, INFRA> {
  abstract toEntity(payload: INFRA): APP;
  abstract toPersistence(entity: APP): INFRA;
}

Implementações ficam em src/infra/database/knex/mappers/ e são exportadas como singleton (ex.: QueueMapper). Exemplo simplificado:

// src/infra/database/knex/mappers/queue.ts
class Mapper implements MapperGateway<QueueEntity, Queue> {
  toEntity(payload: Queue): QueueEntity {
    return new QueueEntity({ /* campos de negócio */ }, payload.CD_FILA);
  }
  toPersistence(entity: QueueEntity): Queue {
    return { CD_FILA: entity.getId(), DESCRICAO: entity.getDescription(), /* ... */ };
  }
}
export const QueueMapper = new Mapper();

Mappers frequentemente usam helpers de knex/utils.ts (bitToBoolean, booleanToBit, prepareDatesForMssql).

Comportamentos específicos MSSQL

  • prepareDatesForMssql: normaliza datas antes do insert/update.
  • removePrimaryKey: remove PK do payload no insert quando o ID é gerado pelo banco.
  • selfIdGeneration: quando true, gera ID via MAX(primaryKey) + 1 antes do insert.
  • Tipos bit do SQL Server são convertidos via bitToBoolean / booleanToBit.

Gateway mínimo (só CRUD)

// src/app/gateways/queue.repository.gateway.ts
export abstract class QueueRepositoryGateway extends DefaultRepositoryGateway<
  QueueEntity,
  Queue,
  'CD_FILA'
> {}

Repository mínimo (só constructor)

// src/infra/database/knex/repositories/knex-queue.repository.ts
@Injectable()
export class KnexQueueRepository
  extends KnexDefaultRepository<QueueEntity, Queue, 'CD_FILA'>
  implements QueueRepositoryGateway
{
  constructor(companyDbGenerator: CompanyKnexProvider) {
    super(companyDbGenerator, 'FILA', 'CD_FILA', QueueMapper);
  }
}

Gateway estendido (CRUD + queries)

Quando a feature precisa de buscas além do CRUD, o gateway estende DefaultRepositoryGateway e adiciona métodos abstract:

// src/app/gateways/product.repository.gateway.ts (conceitual)
export abstract class ProductRepositoryGateway extends DefaultRepositoryGateway<
  ProductEntity, Product, 'CD_PRODUTO'
> {
  abstract findByIds(companyId: number, productIds: number[]): Promise<ProductEntity[]>;
}

O KnexProductRepository estende KnexDefaultRepository e implementa os métodos extras com queries Knex adicionais.

Fluxo CRUD completo

Próximos tópicos

On this page