Padrões de projeto, princípios e arquitetura. Filtre e explore.
88 de 88
Cria famílias inteiras de objetos relacionados através de uma interface única, garantindo que os produtos de uma mesma variante sempre combinem entre si.
Converte a interface de uma classe em outra que o cliente espera, permitindo que classes incompatíveis trabalhem juntas.
Um agregado é um grupo de objetos que muda junto e precisa ser consistente junto, tratado como uma única unidade por trás de uma raiz. Só a raiz é acessada de fora; ela guarda as invariantes que valem para o conjunto. É a fronteira que responde à pergunta mais difícil da modelagem: o que precisa estar consistente no mesmo instante, e o que pode esperar.
Allowlist (lista de permissão) só deixa passar o que foi explicitamente aprovado — origens CORS, IPs de admin, redirect URIs, hosts de webhook. É o oposto da blocklist, que tenta enumerar o mal e sempre atrasa. Não substitui autenticação nem rate limiting: é um corte cedo por identidade de rede ou de origem, não por volume nem por papel de usuário.
Todo sistema novo acaba tendo que conversar com um velho — um ERP legado, uma API de terceiro, um modelo que não se controla. A camada anticorrupção é a fronteira que traduz o modelo do outro para o seu e impede que o vocabulário torto dele entre no seu domínio. É o Adapter elevado à escala de um modelo inteiro: a sujeira fica confinada na borda, e o domínio novo nunca a vê.
Num sistema de muitos serviços, um ponto único de entrada por onde tudo passa: o API Gateway roteia cada requisição ao serviço certo e concentra o que é transversal — autenticação, rate limiting, CORS, log — para os serviços não repetirem isso cada um. O cliente conhece só o gateway, e a topologia interna pode mudar por trás. O risco é o gateway virar gargalo, ponto único de falha, ou um monólito de fronteira inchado de regra.
Uma estrutura onde a única escrita permitida é acrescentar no fim: nada se edita, nada se apaga. Corrigir é gravar um registro novo que supera o anterior — e é assim que funcionam o WAL do seu banco, o Kafka, o git e o seu extrato.
Isola o domínio no centro e o conecta ao mundo externo por portas (interfaces) e adaptadores (implementações), tornando infraestrutura um detalhe plugável.
Oito suposições que quase todo desenvolvedor faz ao chamar código pela rede — a rede é confiável, a latência é zero, a banda é infinita — e que a realidade desmente uma a uma, sempre em produção. Reconhecê-las é o começo de todo sistema distribuído que não desmorona: cada falácia ignorada vira um incidente esperando o pior momento.
Autenticação responde *quem você é*: prova de identidade antes de qualquer permissão. Na web, o desenho padrão é sessão no servidor com cookie httpOnly, Secure e SameSite — o token opaco mora fora do alcance do JavaScript da página. Confundir autenticação com autorização, ou guardar a prova de identidade no localStorage, é o caminho mais curto para vazamento e abuso.
Autorização responde *o que você pode fazer* depois de autenticado. RBAC (roles) e guards/middleware — Nest guards, Next middleware, Express — concentram a decisão perto da rota ou do caso de uso. “Tem `req.user`” não é autorização; IDOR e checagem só no front são as falhas clássicas.
Uma API genérica que tenta servir web, iOS e Android ao mesmo tempo acaba servindo mal a todos. O BFF é um backend por tipo de cliente, moldado às necessidades daquela interface: ele agrega chamadas, enxuga campos e formata a resposta sob medida. Cada frontend ganha um backend que fala a língua dele — ao preço de mais um serviço por cliente para manter, e do risco de duplicar lógica entre eles.
Quando o produtor é mais rápido que o consumidor, alguém tem que ceder. Backpressure é o sinal de 'vá mais devagar' subindo a cadeia: o consumidor dita o ritmo, o produtor respeita, e o buffer entre eles não cresce até estourar a memória. Sem ele, a diferença de velocidade não some — ela vira um buffer infinito, latência crescente e, no fim, um out-of-memory.
Separa uma abstração da sua implementação para que as duas variem independentemente, trocando uma hierarquia que cresce em produto por duas que crescem em soma.
Constrói objetos complexos passo a passo por uma interface fluente, separando a montagem (incremental, com escolhas opcionais) do objeto final (completo e validado).
Recursos compartilhados propagam falha: se todas as chamadas disputam o mesmo pool, a dependência mais lenta consome tudo e derruba as rotas que nem falavam com ela. O anteparo divide o recurso em compartimentos com teto próprio.
Uma cópia rápida de um dado caro de obter. Acelera leitura e alivia o banco — em troca de uma pergunta permanente: como a cópia para de mentir quando a fonte muda? TTL, invalidação e stampede são o conteúdo; a ferramenta (Redis, Memcached, CDN) é só o endereço.
Passa uma requisição por uma corrente de manipuladores; cada um decide se trata ou repassa adiante, sem que o remetente saiba quem vai atender.
Quando uma dependência já está claramente fora do ar, continuar chamando não ajuda ninguém: gasta o seu prazo e atrapalha a recuperação dela. O disjuntor detecta o padrão de falha e passa a recusar na hora, até que valha a pena tentar de novo.
Uma síntese de arquiteturas em camadas concêntricas, governada por uma única regra: as dependências só apontam para dentro. O domínio fica no centro e não conhece banco, framework nem interface; os detalhes ficam nas bordas e dependem do domínio, nunca o contrário. Isso torna o núcleo do sistema testável e imune a trocar de banco ou de framework — ao preço de camadas, interfaces e mapeamentos que só se pagam quando há regra de negócio para proteger.
Transforma uma ação em objeto, com os dados de que ela precisa. Isso permite enfileirar, agendar, registrar e desfazer operações — e desacopla quem pede de quem executa.
Herança promete reúso e entrega acoplamento: a subclasse herda tudo do pai, inclusive o que não queria, e a árvore engessa numa dimensão só. Compor comportamento a partir de peças encaixáveis dá o mesmo reúso sem amarrar quem usa a quem fornece — por isso o conselho antigo de preferir composição, e só herdar quando a relação 'é um' é honesta.
Compõe objetos em árvores e faz o cliente tratar um item isolado e um grupo inteiro exatamente do mesmo jeito, através de uma interface comum.
Quando o dado vive em cópias, mantê-las idênticas a cada instante custa latência e disponibilidade. A consistência eventual troca o 'sempre igual' por 'igual daqui a pouco': as réplicas convergem, e o sistema fica mais rápido e mais disponível — ao preço de janelas em que a leitura vê o passado.
Separa o modelo de escrita (commands) do modelo de leitura (queries), podendo usar stores e serviços distintos otimizados para cada lado.
Todo método deve ser ou um comando que muda estado e não retorna dado, ou uma query que retorna dado sem efeito colateral — nunca os dois ao mesmo tempo.
Uma mensagem que nunca vai dar certo não pode ficar voltando para a fila: ela bloqueia a ordem, consome CPU e esconde o problema. A DLQ é o desvio para onde vai o que falhou demais — com contexto suficiente para diagnosticar e reprocessar.
Adiciona comportamento a um objeto em tempo de execução envolvendo-o em camadas empilháveis que compartilham a mesma interface — composição no lugar de herança.
Módulos de alto nível não devem depender de módulos de baixo nível; ambos devem depender de abstrações — e a abstração pertence a quem a usa, não a quem a implementa.
Empacota o app com tudo que ele precisa numa imagem imutável que roda igual em qualquer máquina. Container é essa imagem em execução — um processo isolado pelo kernel, não uma máquina virtual.
Três lembretes contra o excesso: DRY (não repita a mesma regra), KISS (não complique além do necessário) e YAGNI (não construa o que talvez nunca use). Parecem óbvios e são justamente por isso perigosos — DRY apressado acopla o que só parecia igual, e YAGNI mal lido vira desculpa para não pensar. O valor está em saber quando cada um se aplica.
Esconder o estado atrás de um contrato: quem usa o objeto não sabe — nem depende de — como ele guarda dados por dentro. Sem isso, cada mudança de representação vaza para quem consome; com isso, o objeto protege suas invariantes e pode mudar a implementação sem quebrar o resto.
Em vez de guardar o estado atual, o sistema guarda a sequência imutável de eventos que o produziu — o estado é sempre derivado por replay, e o histórico é a fonte da verdade.
Oferece uma interface simples e de alto nível para um subsistema complexo, escondendo a coreografia entre as peças atrás de um único ponto de entrada.
Define uma interface para criar objetos, deixando as subclasses decidirem qual classe concreta instanciar.
Dois modelos de mensageria que parecem iguais e resolvem problemas opostos. A fila divide trabalho: uma mensagem é processada por um único consumidor entre vários. O pub/sub difunde um fato: um evento chega a todos os inscritos, cada um com sua cópia. Escolher errado ou duplica trabalho que devia ser único, ou deixa de avisar quem precisava saber.
Reduz o consumo de memória compartilhando entre muitos objetos a parte do estado que é igual em todos, mantendo fora apenas o que varia de instância para instância.
At-most-once perde, at-least-once duplica, e exactly-once não existe entre sistemas distintos. Entender qual das três você tem — e por que a terceira é vendida sem ser verdade — é o que decide se o cliente vai ser cobrado duas vezes.
Senhas de banco, chaves de API e tokens de assinatura não pertencem ao Git nem a `.env` commitado. Gestão de segredos é guardar, injetar, rotacionar e auditar essas credenciais — cofres (Vault, Secrets Manager, Doppler) e identidade da carga no lugar de senhas eternas em variáveis soltas. Sem isso, um leak do repositório ou de um dump de env compromete produção inteira.
Executar a mesma operação duas vezes tem o mesmo efeito de executar uma: é o que torna o retry seguro. Em rede, onde 'não sei se foi' é resposta comum, é a diferença entre reenviar e cobrar em dobro.
Entrega confiável quase sempre significa 'ao menos uma vez' — e ao menos uma vez quer dizer que a mesma mensagem pode chegar duas. A inbox é a memória do consumidor: registra o id de cada mensagem já processada e ignora a repetida, transformando um canal que duplica num efeito que acontece uma vez só. É o par consumidor do Outbox, e o jeito prático de sobreviver ao 'exactly-once é mentira'.
A diferença entre achar uma linha em milhões e varrer todas elas. Um índice é uma cópia ordenada de uma coluna que troca busca linear por busca binária — a estrutura que faz a query voar. O preço vem em cada escrita, que agora precisa manter o índice também, e na sutileza de que o índice só serve quando a consulta o consulta do jeito certo.
Representa as regras de uma mini-linguagem como uma árvore de objetos, com uma classe por regra gramatical, e avalia frases dessa linguagem percorrendo a árvore.
Nenhum cliente deve ser forçado a depender de métodos que não usa: é melhor várias interfaces pequenas e específicas do que uma grande e genérica.
Oferece uma forma de percorrer os elementos de uma coleção sequencialmente sem expor como ela é construída por dentro — e permite mais de uma travessia simultânea.
JSON Web Token é um formato de credencial assinada: o servidor (ou o IdP) embute claims e uma assinatura, e quem recebe verifica sem necessariamente consultar um store de sessão. Útil para APIs e SSO — perigoso quando vira “auth completo” com token eterno no localStorage, algoritmo `none`, ou sem plano de refresh e revogação.
Você declara o estado desejado — '3 réplicas desta imagem, com este tanto de memória' — e o cluster trabalha continuamente para a realidade convergir. Pod é a menor unidade: um ou mais containers que vivem e morrem juntos.
Em vez de um saldo que muda, um histórico imutável de lançamentos — e o saldo é a soma. Com partidas dobradas, todo valor sai de uma conta e entra em outra, e a soma do sistema é sempre zero: dinheiro não aparece nem some sem rastro.
Sistemas espelham a estrutura de comunicação de quem os constrói. Dois times que mal se falam produzem uma integração pobre entre seus módulos — não por falha técnica, mas por organograma. A consequência prática é poderosa: para obter a arquitetura que você quer, muitas vezes é preciso primeiro organizar os times na forma dessa arquitetura.
Fale apenas com os amigos próximos. Um método deve interagir só com o próprio objeto, seus campos, seus parâmetros e o que ele mesmo cria — nunca navegar por dentro do que um objeto devolveu. Cada ponto a mais numa cadeia (a.getB().getC().getD()) é um acoplamento à estrutura interna de outro, que quebra quando essa estrutura muda.
Duas formas de impedir que duas escritas concorrentes se apaguem. O pessimista trava antes e faz todo mundo esperar; o otimista não trava nada e descobre o conflito na hora de gravar. A escolha é sobre a probabilidade de conflito, não sobre preferência.
Um subtipo deve poder substituir seu supertipo sem que o código que usa o supertipo perceba a diferença — o que é uma exigência sobre comportamento, não apenas sobre assinatura.
Declare os estados possíveis e as transições permitidas numa tabela explícita: tudo que não está nela é impossível por construção. É a diferença entre 'status é uma string que a gente atualiza' e um ciclo de vida com regras.
Concentra num objeto as regras de interação entre vários componentes, que passam a conversar através dele em vez de se conhecerem diretamente.
Captura o estado interno de um objeto num instantâneo opaco que pode ser guardado e restaurado depois, sem expor os detalhes desse estado a quem o guarda.
Autenticação multifator exige mais de uma prova independente — tipicamente algo que você sabe (senha) e algo que você tem (TOTP, chave de segurança). MFA reduz conta tomada por senha vazada; não impede XSS em sessão já aberta, phishing bem feito contra códigos, nem falhas de autorização. Backup codes e recuperação são parte do desenho, não um pós-escrito.
Dividir o sistema em serviços pequenos e autônomos, cada um com seu deploy, seu banco e sua escala. Compra autonomia — times e serviços evoluem independentes — e cobra caro: a rede entra no meio de tudo, transações viram sagas, e cada serviço vira um sistema a operar. É uma troca de complexidade de código por complexidade operacional, e só vale quando a autonomia resolve um problema que você de fato tem.
O meio-termo que a moda dos microsserviços fez esquecer: um único deploy, mas com módulos de fronteiras internas reais, cada um escondendo suas entranhas atrás de uma API pública. Dá a maior parte do benefício da modularização — responsabilidades separadas, mudanças isoladas — sem pagar a rede, a consistência eventual e o custo operacional de distribuir. Costuma ser o ponto de partida certo, e o destino final de muita gente.
Transações concorrentes se atrapalham de formas específicas e nomeadas. O nível de isolamento é a escolha de quais dessas anomalias o banco vai impedir — e cada degrau a mais custa concorrência, ou custa transações abortadas.
OAuth 2.0 é delegação de acesso: um cliente obtém autorização para agir em nome do usuário sem ver a senha dele. O fluxo moderno é Authorization Code com PKCE; o implícito morreu. OIDC acrescenta identidade (id_token) em cima. Inventar “OAuth caseiro” com senha no body de um SPA costuma ser pior que sessão clássica bem feita.
Define uma dependência um-para-muitos: quando um objeto muda de estado, todos os seus dependentes são notificados automaticamente.
Módulos devem ser abertos para extensão e fechados para modificação: adicionar comportamento novo deveria significar escrever código novo, não reabrir e alterar o que já funciona.
Para escalar, um fluxo de mensagens é dividido em partições — e aí a ordem deixa de ser global. A chave de partição decide em qual partição cada mensagem cai, e a ordem só é garantida dentro de uma partição. Escolhê-la é um equilíbrio delicado: agrupar o que precisa de ordem, espalhar o resto para o paralelismo, e evitar que uma chave concentrada vire uma partição quente.
Um mesmo contrato, várias implementações: o código chama a abstração e o comportamento concreto chega em runtime. Herança é um caminho para isso — não o único. Sem polimorfismo, cada variação vira `if/switch` no cliente; com ele, novas variantes entram sem reabrir quem chama.
Cria objetos novos copiando um modelo já configurado, em vez de construí-los do zero — útil quando montar a configuração é caro ou quando o tipo concreto não deve ser conhecido.
Coloca um substituto com a mesma interface na frente de um objeto para controlar o acesso a ele — adiando a criação, cacheando resultados, checando permissão ou falando com um objeto remoto.
Duas execuções leem o mesmo estado, decidem com base nele e gravam — e o resultado passa a depender de quem chega primeiro. É o bug que nunca aparece no teste e vende o mesmo ingresso duas vezes na sexta à noite.
Capacidade é finita, e demanda não pede licença. Limitar a taxa é escolher conscientemente quem é recusado quando a demanda passa do que se aguenta — em vez de deixar a sobrecarga escolher, degradando o serviço para todo mundo ao mesmo tempo.
Quando as leituras afogam o banco antes das escritas, a saída barata é copiar: o primário recebe todas as escritas e as replica para nós somente-leitura que absorvem as consultas. Escala leitura sem tocar no modelo — ao preço de um atraso de replicação que faz as réplicas verem o passado por um instante.
Uma coleção de objetos de domínio que finge não ter banco atrás. O repositório expõe operações na linguagem do negócio — 'pedidos pendentes há mais de uma hora' — e é o único lugar que sabe como isso vira consulta.
Falhas passageiras se resolvem sozinhas se você esperar um pouco e tentar de novo. Mas repetir do jeito errado — sem espera crescente, sem ruído aleatório, sem filtrar o que é repetível — transforma um soluço em uma queda.
Coordena uma transação distribuída de longa duração como uma sequência de transações locais, cada uma com uma ação compensatória para desfazer semanticamente em caso de falha.
Quando os dados não cabem mais numa máquina — nem em disco, nem em escrita —, a saída é reparti-los: cada shard guarda um pedaço, escolhido por uma chave. Escala escrita e volume de forma horizontal, ao preço alto de perder as junções fáceis, as transações entre shards e a chance de errar a chave e nunca mais conseguir corrigir barato.
Garante que uma classe tenha uma única instância no processo e oferece um ponto global de acesso a ela — uma conveniência que cobra caro em acoplamento e testabilidade.
Um módulo deve ter apenas uma razão para mudar — ou seja, deve responder a um único interessado do negócio, e não acumular decisões que evoluem por motivos diferentes.
Transforma cada estado de um objeto em uma classe própria: o comportamento muda conforme o estado atual, e é o próprio estado que decide a próxima transição.
A alternativa à reescrita big-bang, que quase sempre fracassa. Em vez de parar tudo e virar a chave num dia, coloca-se um roteador na frente do sistema antigo e migra-se pedaço a pedaço: cada rota reescrita passa a ir ao novo, o legado encolhe, e o usuário nunca vê uma virada de chave. O nome vem da figueira que cresce sobre a árvore hospedeira até substituí-la — e o custo é conviver com os dois sistemas durante a transição.
Encapsula uma família de algoritmos intercambiáveis por trás de uma interface comum, deixando o cliente trocar a estratégia em tempo de execução.
Define o esqueleto de um algoritmo numa classe base e deixa que as subclasses preencham passos específicos, sem poder alterar a ordem em que eles acontecem.
Quando a rede parte um sistema distribuído em dois, você só pode manter uma entre consistência e disponibilidade — não as duas. O CAP não é um menu de três opções: é uma escolha forçada que só aparece durante a partição, e que a maioria dos sistemas afina por operação, não de uma vez.
Todo pedido que sai da sua máquina precisa de um prazo máximo. Sem ele, a falha mais comum em produção não é o erro — é a espera infinita, que consome recursos até derrubar quem ainda estava saudável.
Salvar no banco e publicar um evento são duas operações em dois sistemas — e não há transação que cubra as duas. O outbox transforma o evento numa linha da mesma transação, e deixa a publicação para depois.
Como fazer vários bancos commitarem uma transação juntos — ou nenhum. Um coordenador pergunta 'estão prontos?' (prepare), e só manda 'commit' se todos prometeram. Dá atomicidade entre sistemas, ao preço de recursos travados durante todo o protocolo e de um coordenador que, se cair na hora errada, deixa todo mundo travado sem saber o que fazer.
Uma operação de negócio quase nunca é uma escrita só: salva o pedido, baixa o estoque, registra o log. O Unit of Work acumula todas essas mudanças durante a operação e as confirma juntas, numa transação única — ou tudo, ou nada. Ele tira de cada repositório a decisão de quando gravar e a centraliza num commit atômico, evitando o estado meio-aplicado.
Nem tudo no domínio tem identidade. Dez reais é dez reais; um CEP é um CEP — não existe 'este' e 'aquele', existe o valor. Um Value Object é um tipo definido só pelo seu conteúdo, imutável e com igualdade por valor, que carrega as regras daquele conceito em vez de deixá-las espalhadas sobre um number ou string cru. É o antídoto da obsessão por primitivos.
Separa operações da estrutura de objetos sobre a qual elas atuam, permitindo adicionar comportamentos novos sem modificar as classes existentes — ao custo de congelar o conjunto de tipos.
Uma fatia virtual de um servidor físico, com root, IP público e nenhuma opinião: você instala, configura, protege e opera tudo. É o chão que explica os andares de cima — container, orquestrador e PaaS são abstrações disto aqui.
Em vez de perguntar 'já aconteceu?' de minuto em minuto, você registra uma URL e o outro sistema te chama quando acontece. É o Observer atravessando a internet — com tudo que a rede cobra por isso: assinatura, retry e duplicata.