🏔️ Minas Gerais · desde sempre

Nem SQL, nem NoSQL. Uma tabela. Um tipo. Todas as colunas opcionais. A metodologia que Minas Gerais guarda há trezentos anos, agora explicada estação por estação pra quem escreve software.

🕰️ Partidas · Linha Trem--:--
  1. 07:00A guerra bestaEst. 1No horário
  2. 07:30As duas regrasEst. 2No horário
  3. 08:00Os valoresEst. 3No horário
  4. 08:45Fundamentação técnicaEst. 4No horário
  5. 09:30Laboratório do tremEst. 5No horário
  6. 10:15O trem e a IAEst. 6No horário
  7. 10:16Kombi tunada com neonEst. 7No horário
  8. 11:00Das heresiasEst. 8No horário
  9. 11:30Perguntas frequentesEst. 9No horário
  10. 12:00O juramentoEst. 10No horário
Estação 1 · 07:00

A guerra besta

Faz vinte anos que a indústria briga entre SQL e NoSQL. Cada lado ficou com metade da verdade. Lá nas montanhas, tomando café, Minas Gerais já tinha somado as duas metades.

🗄️ SQL

  • ✅ Tipos de verdade
  • ✅ Joins
  • ❌ Migration toda semana
  • ❌ O DBA está de férias

📄 NoSQL

  • ✅ Flexível
  • ✅ Sem migration
  • ❌ nome, name, nomeCompleto, NOME
  • ❌ Ninguém sabe o que tem dentro

🚂 Trem

  • ✅ Tipos de verdade
  • ✅ Joins (de trem com trem)
  • ✅ Flexível como documento
  • ✅ Nenhuma migration quebra nada
  • ✅ Acompanha café

O mineiro nunca precisou de mais de uma palavra pra classificar o universo. Cadeira é trem. Carro é trem. Sentimento é trem. Documento fiscal é trem. O mineiro inventou o tipo universal antes da ciência da computação existir.

Estação 2 · 07:30

As duas regras

A metodologia inteira cabe em duas regras. As duas são inegociáveis.

🗄️ Regra 1 · Banco de dados

Existe uma única tabela, e ela se chama trem.

schema.sql
CREATE TABLE trem (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  trem_id     uuid REFERENCES trem(id),
  origem_id   uuid REFERENCES trem(id),
  destino_id  uuid REFERENCES trem(id),
  nome        text,
  email       text,
  preco_em_centavos bigint,
  quantidade  integer,
  embedding   vector(1536),
  criado_em   timestamptz DEFAULT now(),
  deletado_em timestamptz
  -- e quantas colunas o trem precisar.
  -- Todas opcionais.
);
⚙️ Regra 2 · Aplicação

Existe um único tipo, e ele se chama Trem.

trem.ts
export type Trem = {
  id?: string;
  trem_id?: string;
  origem_id?: string;
  destino_id?: string;
  nome?: string;
  email?: string;
  preco_em_centavos?: number;
  quantidade?: number;
  embedding?: number[];
  criado_em?: Date;
  deletado_em?: Date;
  // espelho fiel da tabela trem.
  // Todos opcionais.
};

É trem de ponta a ponta

🗄️Bancotrem
⚙️Back-endTrem
📱Front-endTrem
🧪TestesTrem
📊DiretoriaTrem
Estação 3 · 08:00

Os valores

No espírito do Manifesto Ágil, que, reparem, também é um manifesto só, declaramos que valorizamos:

Mesmo reconhecendo valor nos itens à direita, a gente fica com o trem.

Estação 4 · 08:45

Fundamentação técnica

Dez argumentos, sustentados pelo PostgreSQL, pelo TypeScript e pelas maiores empresas de tecnologia do mundo. Leia com calma e um café do lado.

🦆

Duck typing no banco

Ninguém declara o que é cliente e o que é pedido. O pedido é pedido porque tem preço. Um trem é definido pelo que ele carrega.

SELECT pedido.*
FROM trem AS cliente
JOIN trem AS pedido
  ON pedido.trem_id = cliente.id
WHERE cliente.email = 'ze@uai.com.br'
  AND pedido.preco_em_centavos IS NOT NULL;
🏢

A indústria chegou lá pela metade

  • Bigtable, HBase e Cassandra são tabelas esparsas com colunas quase sempre vazias.
  • A AWS recomenda single-table design no DynamoDB.
  • O Rails tem Single Table Inheritance há quase vinte anos.
  • O Protobuf 3 removeu os campos required.
  • No GraphQL, campo aceita nulo por padrão.

Cada um achou um pedaço. Minas achou a verdade inteira, e com café.

🙏

Toda coluna opcional é honestidade

No mundo real, todo dado pode faltar. O endereço veio sem número porque "é a casa amarela depois da venda do Seu Tião". Um NOT NULL é uma promessa que o mundo não vai cumprir.

Trem não é any. O any desiste de tipar. O Trem sabe o que pode ter, só não exige que tenha.

🔄

Migrations nunca mais quebram nada

No PostgreSQL, adicionar coluna nula sem default só mexe em metadados. É instantâneo. Cliente antigo ignora a coluna nova, cliente novo usa. Compatibilidade pra frente e pra trás, de fábrica.

ALTER TABLE trem ADD COLUMN cor text;
-- pronto. ninguém quebrou.
🫥

Nulo sai de graça

No PostgreSQL, um valor nulo gasta um bit no null bitmap da linha. O vazio no trem é quase de graça, igual ao silêncio mineiro. Pra performance, cada "entidade" ganha seu índice parcial.

CREATE INDEX trem_pedidos ON trem (trem_id)
WHERE preco_em_centavos IS NOT NULL;
🕸️

Muitos-para-muitos é trem

Um trem que liga um trem a outro trem é um trem.
INSERT INTO trem (origem_id, destino_id)
VALUES ('aluno', 'disciplina');

De quebra, a tabela vira também um banco de grafos.

🧠

Metade da Ciência da Computação resolvida

"Só existem duas coisas difíceis em Ciência da Computação: invalidação de cache e dar nome às coisas." (Phil Karlton)

Ninguém mais discute User contra Usuario, nem singular contra plural. É trem. A invalidação de cache fica pra depois.

⏱️

Onboarding em um segundo

Dev novo pergunta: "Qual é o modelo de dados?"

Resposta: "Trem."

Onboarding concluído. Pode pegar a primeira task.

🧪

Testes sem fixture

O menor trem válido é o objeto vazio. Ele compila e entra no banco. Todo teste começa dele e acrescenta só o que precisa.

const tremPrimordial: Trem = {};
INSERT INTO trem DEFAULT VALUES;
🧩

TypeScript é estrutural, e o trem também

No TypeScript, o que define um tipo é a forma, e não o nome. No trem, o que define um pedido é ter preço. Banco e linguagem pensam igual. O ORM fica com um model só e a impedância objeto-relacional some.

Estação 5 · 09:30

Laboratório do trem

Engate os vagões que quiser e veja o que o seu trem é. Não importa a combinação: continua sendo Trem, e continua compilando.

🚃 Engate as colunas

🧑
Um usuárioTem email. Pelo que carrega, é gente.
✅ type Trem: compila✅ INSERT válido✅ Zero migrations

TypeScript


        

SQL


      

🔍 Detector de heresias

Cole um trecho de SQL ou TypeScript e o Concílio julga na hora.

⚖️
Aguardando julgamentoClique em Julgar.
Estação 6 · 10:15

O trem e a inteligência artificial

O software de hoje é escrito em parceria com IA. É aqui que o trem deixa de ser só uma boa ideia e vira uma ideia necessária.

100% de chance de a IA acertar o nome da tabela. Existe uma só. Nenhum outro paradigma de modelagem oferece essa garantia matemática.
Me descreve o modelo de dados inteiro do sistema?
Trem.
Só isso?
Uai, e precisa de mais? 🚂
  • 🎯
    A IA nunca inventa um tipo que não existe

    Se ela escrever Pedido ou CustomerDTO, o compilador reclama na hora. O único tipo é Trem.

  • 🤖
    Agentes mexem no schema com segurança

    Precisa de campo novo? ADD COLUMN, opcional. Nenhum cliente quebra e nenhuma migration destrutiva roda.

  • 📆
    Requisito muda toda semana

    Schema rígido é uma aposta de que o futuro vai ser igual ao presente. Essa aposta perde sempre. O trem não aposta, aceita.

  • 🧭
    Embeddings já têm lugar

    Todo trem pode ter um vetor. Busca semântica vira ORDER BY embedding <=> $1 na mesma tabela de sempre. RAG de trem sobre trem.

  • 🪶
    Contexto menor

    Descrever o modelo de dados inteiro pra um LLM custa uma palavra.

Estação 7 · 10:16

Kombi tunada com neon

A Metodologia Trem foi feita pra desenvolvimento muito rápido. Tão rápido quanto o veículo mais rápido do planeta. Largue a corrida e confira.

✈️ Caça da FAB✈️
🛸 OVNI de 1986🛸
🚐 Kombi tunada com neon🚐
Histórico oficial: a Kombi venceu 100% das corridas registradas.
  1. Na Noite Oficial dos OVNIs, em 19 de maio de 1986, a FAB mandou caças atrás de objetos voadores no céu do Brasil. Os caças não alcançaram os OVNIs. Logo, OVNI é mais rápido que jato.
  2. Os OVNIs foram vistos no planeta. Tudo o que é visto no planeta é do planeta. Logo, os OVNIs são do planeta e disputam o título.
  3. Não existe nenhum registro, em nenhum arquivo oficial de nenhum país, de um OVNI ultrapassando uma Kombi tunada com neon.
  4. Logo, a Kombi tunada com neon é mais rápida que os OVNIs, que são mais rápidos que os jatos. ∎
  5. O dev que adota a Metodologia Trem passa pelos concorrentes presos em migration como uma Kombi passa por um OVNI: sem registro, sem testemunha e com o neon piscando.
Estação 8 · 11:00

Das heresias

O trem é um só. Qualquer desvio disso, por menor que seja, deturpa o Manifesto.

HeresiaPor que é heresia
CREATE TABLE outro_tremÉ uma segunda tabela. O nome não salva ninguém.
CREATE TABLE trem_2Mesma coisa, com a agravante do número.
CREATE TABLE trensO plural é uma confissão: se tem mais de um, não é o trem.
CREATE TABLE trem_logLog de trem é trem. Vai pra tabela trem.
CREATE TABLE vagaoNinguém se engana: vagão é trem com outro nome.
type TremDTOUm DTO de trem é um trem.
interface ITremO I na frente não muda nada. É outro tipo.
type Trem = anyIsso não é trem, é desistência.

⚖️ Casos julgados pelo Concílio de Tiradentes

✅ Permitido

👀 Views

Uma view é um jeito de olhar pro trem, e olhar pro trem não é criar trem. Materialized views estão em análise, porque guardam dados e o Concílio desconfia disso.

✅ Permitido

🔭 Pick<Trem, 'email'>

Isso não é outro tipo. É o trem visto de lado.

⚔️ Grande Cisma

📋 Tabela de migrations do ORM

Os Ortodoxos registram as migrations na própria tabela trem. Os Reformados toleram _prisma_migrations porque "é do ORM, não é nossa". O Manifesto recomenda o caminho Ortodoxo.

✅ Permitido

🏘️ Um banco por microsserviço

Desde que cada banco tenha só a sua tabela trem. O trem é um só, as estações é que são muitas.

Estação 9 · 11:30

Perguntas frequentes

🐘 E o limite de 1.600 colunas do PostgreSQL?
Se o seu trem precisar de mais de 1.600 colunas, ele ficou grande demais. Mas trem grande é trem bão. Converse com a comunidade do PostgreSQL. Nada de criar uma segunda tabela.
📐 E a normalização? E a Terceira Forma Normal?
A tabela trem está na Forma Normal Mineira, que vem depois de todas as outras: não pode haver redundância entre tabelas quando só existe uma tabela.
🤔 Como eu sei se um trem é um usuário ou um pedido?
Pelo que ele tem. Se tem email, é usuário. Se tem preço, é pedido. Se tem os dois, é um usuário que se vendeu. Teste no Laboratório, na Estação 5.
🏷️ Não seria melhor ter uma coluna tipo?
Até pode ter, é só mais uma coluna opcional. Mas pergunte a um mineiro que tipo de trem é aquele e ele vai responder: "Uai, é trem."
📈 Isso escala?
Escala pelo menos tanto quanto uma Kombi, e sobre a Kombi já sabemos tudo o que precisamos saber (Estação 7).
👥 Meu time vai aceitar?
Seu time já fala "trem" no dia a dia. Falta só admitir isso no banco de dados.
Estação 10 · Fim da linha

O Juramento do Desenvolvedor Trem

  1. Juro solenemente que só vou criar uma tabela, e ela vai se chamar trem;
  2. que só vou declarar um tipo, e ele vai se chamar Trem;
  3. que vou tratar toda coluna como opcional, porque a vida é assim;
  4. que vou fazer join de trem com trem, sem vergonha nenhuma;
  5. que nunca vou criar outro_trem, nem em staging, nem num sábado de madrugada;
  6. e que vou entregar na velocidade de uma Kombi tunada com neon.
Uai.