TDA (Tell Don't Ask)

TDA (Tell Don’t Ask)

Introdução A visão original de Kay para OOP não era “objetos com dados públicos que outros manipulam”, era objetos que trocam mensagens e decidem sozinhos o que fazer com elas. O Tell, Don’t Ask é basicamente um resgate dessa ideia original, porque com o tempo muita gente passou a usar OOP como “structs com getters e setters”, perdendo o encapsulamento de verdade.

Introduction Alan Kay’s original vision for OOP was not “objects with public data that others manipulate,” but rather objects that exchange messages and decide for themselves what to do with them. Tell, Don’t Ask is essentially a revival of this original idea, because over time, many people began using OOP as “structs with getters and setters,” losing true encapsulation.


Ideia Central: Exemplo do Cliente e Carteira

Ask (Perguntar): Eu PERGUNTO o saldo, e EU decido o que fazer com ele.

if (cliente.carteira.saldo >= 50) {
  cliente.carteira.saldo -= 50;
} else {
  console.log("saldo insuficiente");
}

Tell (Dizer): Eu DIGO pro cliente pagar, e ELE decide o que fazer.

// "Tell" — eu DIGO pro cliente pagar, e ELE decide o que fazer
cliente.pagar(50);

class Cliente {
  carteira: Carteira;
  pagar(valor: number) {
    if (this.carteira.saldo < valor) {
      throw new Error("saldo insuficiente");
    }
    this.carteira.saldo -= valor;
  }
}

Core Idea: Customer and Wallet Example

Ask: I ASK for the balance, and I decide what to do with it.

if (customer.wallet.balance >= 50) {
  customer.wallet.balance -= 50;
} else {
  console.log("insufficient balance");
}

Tell: I TELL the customer to pay, and THEY decide what to do.

// "Tell" — I TELL the customer to pay, and THEY decide what to do
customer.pay(50);

class Customer {
  wallet: Wallet;
  pay(amount: number) {
    if (this.wallet.balance < amount) {
      throw new Error("insufficient balance");
    }
    this.wallet.balance -= amount;
  }
}

Repare a diferença de responsabilidade: No “Ask”, quem chama o código precisa saber a regra (“se o saldo for menor, não pode pagar”) e tomar a decisão sozinho. No “Tell”, o próprio objeto conhece sua regra e decide por dentro. Quem chama só diz o que quer que aconteça.

Notice the difference in responsibility: In “Ask,” the caller needs to know the rule (“if the balance is lower, you can’t pay”) and make the decision alone. In “Tell,” the object itself knows its rule and decides internally. The caller only states what they want to happen.


Por que “perguntar” é perigoso Pensa no “Ask” espalhado pelo sistema: toda tela, todo botão, todo endpoint que cobra do cliente vai ter que copiar essa mesma verificação de saldo. Se um dia a regra mudar (por exemplo, “clientes VIP podem ficar com saldo negativo até -R$100”), você precisa caçar todos esses lugares e mudar um por um. É praticamente garantido que algum lugar vai ser esquecido — e aí seu sistema tem um bug de regra de negócio inconsistente. Com “Tell”, a regra mora em um lugar só (Cliente.pagar). Mudar uma vez, resolve todo o sistema.

Why “asking” is dangerous Think about “Ask” scattered throughout the system: every screen, every button, every endpoint that charges the customer will have to copy this same balance check. If the rule changes one day (e.g., “VIP customers can have a negative balance up to -100”), you have to hunt down all those places and change them one by one. It is almost guaranteed that somewhere will be forgotten — and then your system has an inconsistent business logic bug. With “Tell,” the rule lives in one place only (Customer.pay). Changing it once fixes the entire system.


Como isso conecta com Law of Demeter Os dois princípios andam juntos, mas resolvem problemas ligeiramente diferentes:

  • Law of Demeter: não atravesse a estrutura interna de outros objetos (a.b.c).
  • Tell, Don’t Ask: não extraia dados de um objeto pra decidir algo fora dele — mande o objeto decidir. Na prática, quando você aplica bem “Tell, Don’t Ask”, quase sempre acaba respeitando Law of Demeter de graça — porque você para de precisar alcançar o carteira.saldo de fora.

How this connects to the Law of Demeter The two principles go hand in hand, but they solve slightly different problems:

  • Law of Demeter: Do not traverse the internal structure of other objects (a.b.c).
  • Tell, Don’t Ask: Do not extract data from an object to decide something outside of it — tell the object to decide. In practice, when you apply “Tell, Don’t Ask” well, you almost always end up respecting the Law of Demeter for free — because you stop needing to reach for wallet.balance from the outside.

O sinal de alerta pra reconhecer Pergunte-se, olhando pro código: “Estou pegando um dado de dentro de um objeto só pra, logo em seguida, tomar uma decisão (if) ou fazer uma conta com esse dado?” Se sim, é sinal de “Ask”. Pergunte então: “quem deveria saber essa regra: eu (quem está chamando) ou o objeto dono do dado?” Quase sempre é o objeto dono.

The warning sign to recognize Ask yourself while looking at the code: “Am I taking data from inside an object just to immediately make a decision (if) or perform a calculation with that data?” If so, it’s a sign of “Ask.” Then ask: “Who should know this rule: me (the caller) or the object that owns the data?” It is almost always the owner object.


Quando NÃO aplicar ao pé da letra Código que só exibe dados (uma tela mostrando o saldo do cliente) precisa “perguntar” — não tem decisão de negócio envolvida, só exibição. Tell, Don’t Ask é sobre decisões e comportamento, não sobre leitura pura de dados para exibir. Structs/DTOs (objetos que só carregam dados entre camadas, tipo resposta de API) não precisam desse princípio — eles não têm comportamento por definição.

When NOT to apply it literally Code that only displays data (a screen showing the customer’s balance) needs to “ask” — there is no business decision involved, only display. Tell, Don’t Ask is about decisions and behavior, not about pure data reading for display. Structs/DTOs (objects that only carry data between layers, like API responses) do not need this principle — they have no behavior by definition.