Pular para o conteúdo

apps/service-design/query-object-pattern

161 · Padrões de Design de Serviço · ≈ 6 min de estudo

Query Object

Uma consulta com filtros opcionais vira um objeto que se compõe por métodos: cada método acrescenta um predicado, todo valor vira parâmetro numerado e toda coluna vinda de fora passa por uma lista permitida. O objeto só monta o SQL; quem executa é outra peça. Use em listagens e relatórios com filtros combináveis, sem concatenar string e sem SQL injection.

passos
6
arquivos
3
testes
0
tecnologias
5
Infraestrutura realTypeScriptBunElysiaPostgreSQLDocker
Baixar cartão

Cenário

O extrato do banco digital aceita filtros opcionais — tipo (PIX, TED, boleto, cartão), status, faixa de valor, ordenação — e pagina por cursor em tabelas com milhões de lançamentos. O mesmo objeto de consulta monta o relatório regulatório de TEDs a partir de R$ 5.000,00 no mês. Um parâmetro malicioso na ordenação ou no id da conta não pode virar SQL.

Planta

Sequência
6/6
GET /accounts/CC-A/transactions?type=pix,ted&minCents=20000&sort=amount_cents&cursor=...1forAccount, whereTypes, whereAmountBetween, orderBy (lista permitida), after(cursor), limit2sql com $1..$n e params3pool.query(sql, params)4linhas (uma a mais que a pagina)5itens e nextCursor6ClienteAPI de extratoQueryTransactionPostgreSQL
6

6 passos — reproduza para seguir o fluxo

Como funciona

6 passos

Consulta montada como objeto.

Esconde cada passo: lembre antes de tocar para revelar.

Vocabulário compartilhado

build()+1
  1. 01

    QueryTransaction tem um método por filtro (forAccount, whereTypes, whereStatus, whereAmountBetween, wherePeriod); cada um acrescenta um predicado e devolve o próprio objeto

  2. 02

    build() numera os placeholders na ordem dos filtros e devolve { sql, params } — nenhum valor entra no texto do SQL

  3. 03

    orderBy só aceita colunas da lista permitida; qualquer outra gera ErrorQueryInvalid (400)

  4. 04

    Paginação por cursor em (created_at, id): a página N custa o mesmo que a primeira e empates de horário não pulam nem repetem linhas

  5. 05

    O executor lê uma linha a mais que a página para saber se há próxima, sem COUNT(*)

  6. 06

    Faixa de valor inclusiva nas duas pontas; período semiaberto [de, até), para um lançamento à meia-noite não cair em dois meses

Trade-offs

O que se ganha, o que se paga

4 vantagenscada ganho tem um preço4 custos

Vantagens

  • Sem SQL injection por construção

  • Filtros opcionais sem if espalhado

  • build() testável sem banco

  • Mesmo objeto para tela e relatório

Custos

  • Mais código que SQL escrito à mão

  • Consulta complexa (CTE, subquery) fica de fora

  • A abstração pode esconder uma consulta sem índice

  • Cursor exige ordenação estável e índice que a acompanhe

apps/service-design/query-object-pattern

3 arquivos

src/

  • query_transaction.tsO objeto de consulta: predicados, lista permitida, cursor e build()
  • api_transaction.tsExecutor e API: filtros do query string, página e cursor

sql/

  • 01_schema.sqlschemaTransações e o índice que serve extrato e cursor

Executar · com Docker

  1. ./docker.sh up# sobe PostgreSQL
  2. cp .env.example .env# variáveis de ambiente
  3. bun install# dependências
  4. bun run demo# roda o cenário
  5. bun run test# integração contra o serviço real
Requisitos
BunDocker
Sobe junto
PostgreSQL

Por que se relacionam

Teste rápido

Qual destes combina com Query Object Pattern?

Próximo projeto · Padrões de Design de ServiçoStrategy
Esc

↑ ↓ navegarEnter abrir191 resultados