PROJETO DE DISCIPLINA • TYPESCRIPT PARA DESENVOLVIMENTO FRONT-END [26E4_2]

Arquitetura Limpa, SOLID e Mapa da Rubrica

Esta página documenta a engenharia do Nexo, conectando a arquitetura em 3 camadas do projeto de referência pos/App_Nasa aos 16 itens avaliados na rubrica oficial.

16 Itens da Rubrica Mapeados no Código

Filtre por competência para inspecionar os arquivos e contratos TypeScript.

1.1 Tipos primitivos, uniões (|) e tipos literais no domínio
src/domain/entities/financial-primitives.ts

Modela estados e operações financeiras com uniões de literais estritas, impedindo valores mágicos inválidos em tempo de compilação.

export type TransactionType = 'INCOME' | 'EXPENSE' | 'PIX_TRANSFER';
export type PixKeyType = 'CPF' | 'EMAIL' | 'PHONE' | 'RANDOM';
export type OpenFinanceConsentStatus = 'AUTHORIZED' | 'EXPIRED' | 'REVOKED';
1.2 Uso equilibrado entre inferência de tipos e tipagem explícita
src/domain/entities/transaction.entity.ts

Aplica tipagem explícita em contratos públicos (parâmetros e retornos de métodos/UseCases) e aproveita a inferência do compilador em acumuladores locais e constantes `as const`.

public static calculateNetBalance(...items: readonly Transaction[]): number {
  const total = items.reduce((acc, tx) => acc + tx.signedAmount, 0); // inferido como number
  return Number(total.toFixed(2));
}
1.3 Objetos tipados, sobrecarga de funções e regras seguras
src/domain/entities/transaction.entity.ts

Valida propriedades com `Transaction.validateProps` retornando `Either<Failure, Transaction>` e aplica sobrecarga de função em `Transaction.lookupTransaction` (por índice `number` ou por `TransactionCategory`).

public static lookupTransaction(items: readonly Transaction[], index: number): Transaction | undefined;
public static lookupTransaction(items: readonly Transaction[], category: TransactionCategory): readonly Transaction[];
public static lookupTransaction(items: readonly Transaction[], key: number | TransactionCategory) { ... }
1.4 Uniões discriminadas, literais e funções no domínio
src/domain/entities/financial-primitives.ts

Modela meios de pagamento com União Discriminada (`PaymentMethodDetail` por `kind: "pix" | "card" | "open_finance"`), função `describePaymentMethod` e status de orçamento no PierreFinancialEngine.

export type PaymentMethodDetail = PixPaymentDetail | CreditCardPaymentDetail | OpenFinanceDebitDetail;
export function describePaymentMethod(payment: PaymentMethodDetail): string {
  switch (payment.kind) { case 'pix': ... case 'card': ... case 'open_finance': ... }
}
2.1 Interfaces coerentes com os contratos do domínio (ISP / DIP)
src/domain/repositories/repositories.ts

Segrega os contratos da camada Domain em interfaces específicas (`ITransactionRepository`, `IOpenFinanceRepository`, `IPixGatewayRepository` e `IUseCase<Success, Params>`).

export interface IOpenFinanceRepository {
  getConnectedAccounts(): Promise<Either<Failure, readonly BankAccount[]>>;
  syncInstitution(institution: InstitutionCode): Promise<Either<Failure, OpenFinanceSyncReport>>;
  toggleConsent(accountId: string, authorize: boolean): Promise<Either<Failure, BankAccount>>;
}
2.2 Arrays readonly, Tuplas e operadores Spread/Rest (...)
src/domain/entities/financial-primitives.ts

Utiliza tuplas imutáveis para coordenadas bancárias (`[banco, agencia, conta]`), tetos de orçamento (`[metaIdeal, tetoAlerta]`) e QR Code PIX, além de `...spread` em `copyWith` e `...rest` em cálculos.

export type BankCoordinatesTuple = readonly [
  bankCode: string,
  branchNumber: string,
  accountWithDigit: string,
];
export type BudgetThresholdTuple = readonly [idealLimit: number, maxAlertCeiling: number];
2.3 Classes OOP com encapsulamento, visibilidade e membros estáticos
src/domain/entities/bank-account.entity.ts

Protege o saldo (`private _balance`) e o consentimento (`private _consentStatus`) dentro de `BankAccount`, além de contadores e validadores `static` em `Failure` e `PixKeyValidator`.

export class BankAccount {
  private _balance: number;
  private _consentStatus: OpenFinanceConsentStatus;
  public get balance(): number { return this._balance; }
  public credit(amount: number): Either<Failure, number> { ... }
}
2.4 Articulação de interfaces, uniões/interseções (&), funções e classes
src/core/either/either.ts

Articula interseções de metadados (`IdentifiableContract & AuditableContract`), callbacks tipados (`FoldCallback<Input, Output>`) e herança entre `TransactionModel` e `Transaction`.

export type DomainRecordMetadata = IdentifiableContract & AuditableContract;
export type FoldCallback<Input, Output> = (value: Input) => Output;
3.1 Proibição de any e tratamento seguro de unknown com Type Guards
src/data/models/transaction.model.ts

A regra `@typescript-eslint/no-explicit-any: error` proíbe `any`. Todo dado externo entra como `unknown` e passa por `isPlainRecord`, `hasStringField` e `hasNumberField`.

public static fromUnknown(raw: unknown): Either<Failure, TransactionModel> {
  if (!isPlainRecord(raw) || !hasStringField(raw, 'id') || !hasNumberField(raw, 'amount')) {
    return left(new ValidationFailure('Payload de transação inválido.'));
  }
  return right(new TransactionModel({ ... }));
}
3.2 Operadores keyof, typeof e asserções de tipo seguras
src/core/types/advanced-types.ts

Usa `keyof` para acesso seguro a propriedades (`getPropertyByKey`, `pickObjectKeys`) e `typeof CATEGORY_METADATA` para derivar tipos a partir de constantes imutáveis.

export type CategoryMetadataMap = typeof CATEGORY_METADATA;
export function getPropertyByKey<T extends object, K extends keyof T>(target: T, key: K): T[K] {
  return target[key];
}
3.3 Generics reutilizáveis preservando tipos nas operações
src/core/usecase/usecase.ts

Implementa `Either<L, R>`, `IUseCase<SuccessType, Params>` e `FlavorConfig.getProfileFor<F extends AppFlavor>(flavor: F)` eliminando duplicação de contratos.

export interface IUseCase<SuccessType, Params> {
  execute(params: Params): Promise<Either<Failure, SuccessType>>;
}
3.4 Mapped Types, Conditional Types e validação de exaustividade (never)
src/core/config/flavor-config.ts

Emprega `DeepReadonly<T>`, `EntityPatch<T>`, `FlavorGatewayContract<F>` (Conditional Type) e `assertNever(value: never): never` nos switches de Enums e chaves PIX.

export type FlavorGatewayContract<F extends AppFlavor> =
  F extends AppFlavor.PROD ? ProdGatewayContract : DevGatewayContract;

export function assertNever(value: never, context = 'Valor não tratado'): never {
  throw new Error(`${context}: ${String(value)}`);
}
4.1 Configuração estrita do tsconfig.json e compilação do módulo
tsconfig.json

Habilita `strict`, `noImplicitAny`, `strictNullChecks`, `noImplicitOverride`, `noUnusedLocals`, `noFallthroughCasesInSwitch` e `tsc -p tsconfig.build.core.json`.

"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitOverride": true,
"sourceMap": true
4.2 Gestão de pacotes @types/* e emissão de Source Maps (.js.map)
tsconfig.build.core.json

Gerencia `@types/node`, `@types/react` e `@types/react-dom` e gera arquivos `.js.map` e `.d.ts.map` em `dist-core/` via `npm run build:core`.

"outDir": "./dist-core",
"sourceMap": true,
"declaration": true,
"declarationMap": true
4.3 Recursos modernos de ECMAScript (ES2022) e Enums pertinentes
src/core/enums/enums.ts

Define os enums `AppFlavor`, `TransactionCategory` e `InstitutionCode` integrados a helpers exaustivos e recursos modernos do ES2022.

export enum AppFlavor { DEV = 'DEV', PROD = 'PROD' }
export enum InstitutionCode { NUBANK = 'NUBANK', ITAU = 'ITAU', INTER = 'INTER', ASAAS = 'ASAAS' }
4.4 Refatoração de JS legado para TS e análise objetiva de IA
src/legacy/legacy-reconciliation.ts

Documenta e testa a migração do cálculo de conciliação financeira de JS para TS estrito e registra 4 revisões técnicas sobre sugestões de IA.

const result = RefactoredReconciliationService.reconcileBatch(rawExternalBatch);
// Retorna Either<Failure, ReconciliationSummary> com tupla [gross, fee, net]

Seção 5 • Evidência de Evolução e Refatoração (JS Legado → TS Estrito)

Comparativo prático em src/legacy/legacy-reconciliation.ts corrigindo coerção de string, propagação de NaN e falta de exaustividade.

ANTES — JavaScript sem Tipagem Estática
// [ANTES — JavaScript Legado sem tipagem estática]
// Problemas detectados durante a migração para TypeScript:
// 1. Parâmetro 'items' sem contrato (aceitava objetos malformados ou strings numéricas).
// 2. Soma 'total += item.amount' concatenava strings ("1000250.50") quando a API retornava string.
// 3. 'item.amount * item.feeOverridePercent' gerava NaN quando feeOverridePercent era undefined.
// 4. Switch de 'tier' sem default exaustivo retornava taxa undefined para novas modalidades.

function reconcilePaymentsLegacy(items) {
  let totalGross = 0;
  let totalFees = 0;

  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    totalGross += item.amount; // BUG 1: concatenação de string se amount for "250.50"

    let rate;
    if (item.feeOverridePercent !== null) {
      rate = item.feeOverridePercent; // BUG 2: se for undefined, passa no !== null e gera NaN!
    } else if (item.tier === 'STANDARD_PIX') {
      rate = 0;
    } else if (item.tier === 'PRIORITY_ASAAS') {
      rate = 0.0099;
    }
    // BUG 3: 'OPEN_FINANCE_DIRECT' não tratado -> rate fica undefined -> totalFees vira NaN
    totalFees += item.amount * rate;
  }

  return {
    gross: totalGross,
    fees: totalFees,
    net: totalGross - totalFees,
  };
}
DEPOIS — TypeScript Estrito com unknown + assertNever + Either
// [DEPOIS — TypeScript Estrito com Clean Architecture, unknown Type Guards e assertNever]
export class RefactoredReconciliationService {
  public static resolveTierFeeRate(tier: SettlementTier): number {
    switch (tier) {
      case 'STANDARD_PIX':
        return 0;
      case 'PRIORITY_ASAAS':
        return 0.0099;
      case 'OPEN_FINANCE_DIRECT':
        return 0.0045;
      default:
        return assertNever(tier, 'Modalidade de liquidação desconhecida');
    }
  }
  // ... validação com Type Guards sobre unknown e retorno Either<Failure, ReconciliationSummary>
}

Seção 6 • Registro e Avaliação Técnica do Uso de IA

Decisões técnicas onde sugestões padrão foram ajustadas para preservar a segurança de tipos, o encapsulamento OOP e a simplicidade da arquitetura.

Desserialização de payloads do localStorage e Webhook Asaassrc/data/models/transaction.model.ts

Sugestão Inicial: Usar `const data = JSON.parse(raw) as any` ou `as TransactionModel[]` direto na leitura do armazenamento para economizar código.

Problema Identificado: O cast direto (`as TransactionModel[]`) oculta dados corrompidos em tempo de execução e perde os métodos da prototipagem da classe `TransactionModel`, além de violar a proibição de `any`.

Decisão Adotada: Implementamos `safeJsonParse(raw): Either<Failure, unknown>` combinado com `TransactionModel.fromUnknown(item)` usando Type Guards (`isPlainRecord`, `hasStringField`, `hasNumberField`).

Configuração do tsconfig.json no TypeScript 5.9tsconfig.build.core.json

Sugestão Inicial: Reutilizar integralmente o `tsconfig.json` do template base contendo `"erasableSyntaxOnly": true` e sem emissão de Source Maps.

Problema Identificado: A flag `erasableSyntaxOnly` bloqueia o uso de `enum` e propriedades de construtor (`private readonly`), recursos exigidos pelos itens 2.3 e 4.3 da rubrica, além de não gerar arquivos `.js.map`.

Decisão Adotada: Ajustamos o `tsconfig.json` com `"sourceMap": true`, `"noImplicitOverride": true` e criamos `tsconfig.build.core.json` para emitir `.js`, `.d.ts` e `.js.map` verificáveis em `dist-core/`.

Modelagem de Entidades e Controle de Mutabilidadesrc/domain/entities/bank-account.entity.ts

Sugestão Inicial: Definir `BankAccount` e `Transaction` apenas como interfaces planas com propriedades públicas mutáveis (`balance: number`).

Problema Identificado: Interfaces planas mutáveis permitem que a camada de UI altere `account.balance -= 500` diretamente, ignorando a verificação de consentimento do Open Finance (`isAuthorized`) e regras de saldo negativo.

Decisão Adotada: Modelamos `BankAccount` e `Transaction` como classes OOP com campos encapsulados (`private _balance`, `private _consentStatus`, `protected readonly _rawAmount`) e métodos que retornam `Either<Failure, number>`.

Controle de Complexidade em Tipos Condicionais e Genéricossrc/core/types/advanced-types.ts

Sugestão Inicial: Criar um tipo condicional recursivo de 4 níveis para inferir rotas, payloads e headers HTTP simultaneamente.

Problema Identificado: Tipos condicionais excessivamente aninhados prejudicam a legibilidade das mensagens de erro do compilador sem trazer ganho real ao domínio financeiro.

Decisão Adotada: Mantivemos apenas tipos utilitários com propósito claro no domínio: `FlavorGatewayContract<F>`, `UnwrapEitherSuccess<T>`, `DeepReadonly<T>` e `EntityPatch<T>`.