☕ Um Café no Bellacosa Mainframe
Gostou do conteúdo? Ajude a manter o café quente, o COBOL compilando e o mainframe acordado. 😄
☕ Pague um café ao Bellacosa

Translate

Mostrar mensagens com a etiqueta Imutabilidade. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta Imutabilidade. Mostrar todas as mensagens

sexta-feira, 18 de outubro de 2024

Monk, COBOL e o Estranho Caso dos 847 Parênteses: Anatomia Técnica de Clojure para Quem Ainda Confia numa Boa DATA DIVISION

 

Bellacosa Mainframe com monk investigando o clojure

☕ Um Café no Bellacosa Mainframe

Monk, COBOL e o Estranho Caso dos 847 Parênteses: Anatomia Técnica de Clojure para Quem Ainda Confia numa Boa DATA DIVISION

🔎 JVM, bytecode, Reader, Compiler, REPL, namespaces, deps.edn, AOT, JAR, EDN, macros, Java interop, imutabilidade, testes e a investigação obsessivamente organizada de uma linguagem em que cada parêntese precisa estar exatamente onde deveria estar

Adrian Monk entrou no CPD às 08h13.

Às 08h14 percebeu que três cabos Ethernet estavam cruzados.

Às 08h15 alinhou os manuais do Db2 pela altura.

Às 08h17 encontrou o verdadeiro problema.

Na tela havia isto:

(defn calcula-saldo [saldo debito]
  (- saldo debito))

Monk ficou imóvel.

— Natalie...

— Sim, Mr. Monk?

— Há quatro parênteses.

— É Clojure.

— Eu sei.

— Então qual é o problema?

Monk aproximou-se da tela.

— Eles precisam fechar na ordem correta.

O programador COBOL sentado ao lado sorriu.

Finalmente havia encontrado alguém que entendia sua obsessão por organização.

Pegue o café.

Na viagem anterior conhecemos a filosofia de Clojure. Agora vamos abrir a máquina, retirar a tampa e descobrir como ela realmente funciona.


1. Antes de tudo: Clojure não é “um script Lisp interpretado”

Essa é provavelmente a primeira ficha que precisamos organizar na mesa de evidências.

A implementação principal de Clojure executa sobre a Java Virtual Machine.

E há uma informação fundamental na documentação oficial:

Clojure não possui interpretador no sentido comum.

O código carregado é compilado para bytecode JVM. Normalmente essa compilação acontece dinamicamente durante o carregamento; Clojure também suporta compilação Ahead-of-Time, ou AOT. (Clojure)

A cadeia mental correta é:

SOURCE CODE
    .clj
      │
      ▼
CLOJURE READER
      │
      ▼
CLOJURE DATA STRUCTURES
      │
      ▼
MACRO EXPANSION
      │
      ▼
CLOJURE COMPILER
      │
      ▼
JVM BYTECODE
      │
      ▼
JVM / JIT
      │
      ▼
MACHINE CODE

Isso já destrói aquela impressão:

Clojure
   =
linguagemzinha interpretada

Não.

Estamos em território JVM.

A versão estável atual, Clojure 1.12.5, lançada em 12 de maio de 2026, continua gerando bytecode compatível com Java 8, embora possa executar em JVMs modernas. (Clojure)


2. Monk encontra o Reader

Aqui Clojure começa a ficar realmente interessante.

Em muitas linguagens imaginamos:

arquivo fonte
     ↓
parser
     ↓
AST
     ↓
compiler

Clojure possui algo chamado:

Reader

Ele transforma caracteres em estruturas de dados Clojure. E isso está ligado a uma propriedade clássica dos Lisps chamada homoiconicidade: programas são representados usando estruturas da própria linguagem. (Clojure)

Veja:

(+ 10 20)

O Reader não enxerga simplesmente uma string contendo código.

Ele reconhece uma lista:

(
  símbolo +
  número 10
  número 20
)

Conceitualmente:

SOURCE

(+ 10 20)

       ↓ Reader

LIST
├── Symbol: +
├── Number: 10
└── Number: 20

Isso é importantíssimo.

Porque ajuda a explicar por que macros Lisp são tão poderosas.

Código já nasce próximo de dados manipuláveis pela própria linguagem.

Monk observa.

— Então primeiro precisamos saber exatamente o que estamos olhando.

— Sim.

— Gostei dessa linguagem.


3. Form não é statement

Outro choque para o COBOLzeiro.

COBOL possui statements:

MOVE
ADD
SUBTRACT
PERFORM
IF
READ
WRITE

Clojure é estruturada fundamentalmente em expressões/forms que produzem valores. A referência oficial ressalta que não existe a mesma distinção tradicional entre declarations e statements; forms são avaliadas e produzem resultados, embora alguns resultados possam ser ignorados quando buscamos apenas efeitos colaterais. (Clojure)

Exemplo:

(+ 10 20)

produz:

30

Até:

(if (> saldo 100)
  "APROVADO"
  "NEGADO")

produz um valor.

Compare com a mentalidade COBOL:

IF WS-SALDO > 100
    MOVE 'APROVADO' TO WS-STATUS
ELSE
    MOVE 'NEGADO' TO WS-STATUS
END-IF.

Clojure:

(def status
  (if (> saldo 100)
    "APROVADO"
    "NEGADO"))

A expressão if retorna aquilo que será associado a status.

Essa pequena diferença influencia enormemente o estilo da linguagem.


4. Nosso primeiro programa: Hello World

Vamos fazer o equivalente ao ritual iniciático de toda linguagem.

COBOL:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. HELLO.

       PROCEDURE DIVISION.

           DISPLAY 'HELLO WORLD'.

           STOP RUN.

Clojure:

(println "Hello World")

Resultado:

Hello World

Monk olha para os dois.

Olha novamente.

— Está faltando alguma coisa.

— Não.

— DATA DIVISION?

— Não precisa.

— PROCEDURE DIVISION?

— Não existe.

— STOP RUN?

— Não.

Monk começa a suar.

😂


5. Agora faça direito: namespace

Aplicação real não deveria ser apenas um arquivo jogado na mesa.

Criamos algo como:

(ns bellacosa.core)

Depois:

(ns bellacosa.core)

(defn -main []
  (println "Hello Bellacosa Mainframe"))

O ns define o namespace.

Namespaces ajudam a organizar:

symbols
functions
Vars
dependencies
aliases

A convenção oficial liga o namespace ao caminho do arquivo. Pontos no nome correspondem a diretórios, e hífens são convertidos em underscores no nome físico do recurso. (Clojure)

Assim:

bellacosa.core

normalmente corresponde a:

src/
└── bellacosa/
    └── core.clj

Isso é convenção importante.

Não invente:

src/
   coisas/
      arquivo-final-agora-vai-3.clj

Monk descobrirá.


6. deps.edn: o pequeno arquivo que organiza a expedição

No ecossistema atual encontramos frequentemente:

deps.edn

EDN significa Extensible Data Notation.

Um projeto mínimo pode ter:

bellacosa/
│
├── deps.edn
│
└── src/
    └── bellacosa/
        └── core.clj

Um deps.edn simples:

{:paths ["src"]
 :deps {}}

Com dependência:

{:paths ["src"]
 :deps
 {org.clojure/clojure
  {:mvn/version "1.12.5"}}}

deps.edn descreve elementos necessários para formar o classpath, incluindo paths, dependências, repositórios e aliases usados pelas ferramentas Clojure CLI/tools.deps. (Clojure)

Para um mainframeiro, pense vagamente:

JCL / PROC / STEPLIB / SYSLIB

versus:

deps.edn
classpath
dependencies
aliases

Não são equivalentes.

Mas ambos respondem, em universos diferentes, à pergunta:

“Onde estão as coisas necessárias para executar isso?”


7. Clojure CLI

Com as ferramentas instaladas você encontrará comandos como:

clj

ou:

clojure

Um deles pode iniciar o REPL.

Você digita:

user=> (+ 10 20)
30

Depois:

user=> (def saldo 1000M)
#'user/saldo

Depois:

user=> (- saldo 200M)
800M

E isso nos leva a uma diferença monumental em relação ao COBOL tradicional.


8. O REPL não é brinquedo

REPL:

READ
EVAL
PRINT
LOOP

O programador COBOL tradicional pensa:

EDIT
 ↓
COMPILE
 ↓
LINK
 ↓
EXECUTE
 ↓
ABEND
 ↓
JES
 ↓
SYSOUT

O desenvolvedor Clojure frequentemente trabalha:

EDITOR
   ↕
REPL
   ↕
PROCESSO VIVO

Ele escreve uma função.

Envia a função ao REPL.

Executa.

Inspeciona dados.

Altera.

Reavalia.

Testa novamente.

Essa interatividade é parte central da cultura Clojure.


9. Editor: onde escrevemos isso?

Clojure não exige uma IDE oficial única.

É comum encontrar:

VS Code + Calva
Emacs + CIDER
IntelliJ IDEA + Cursive
Neovim + LSP

Além disso:

clojure-lsp
nREPL

são peças comuns do ecossistema.

O importante para o iniciante é entender uma coisa:

Escolha um editor com integração REPL decente.

Usar Clojure como se fosse C:

editar arquivo
salvar
executar programa inteiro
olhar resultado

funciona.

Mas você estará ignorando uma das melhores características do ambiente.


10. Compilação dinâmica

Quando Clojure carrega código, ele é compilado.

A documentação é explícita:

não existe a necessidade de um estágio de compilação separado para que uma função deixe de ser “interpretada”; Clojure não possui interpretador tradicional. (Clojure)

Então:

(defn soma [a b]
  (+ a b))

não significa:

guardar texto
↓
interpretar linha
↓
interpretar próxima linha

Existe compilação para JVM bytecode.

A JVM, por sua vez, pode aplicar JIT.

Portanto:

CLOJURE SOURCE
     ↓
CLOJURE COMPILER
     ↓
BYTECODE
     ↓
JVM
     ↓
JIT
     ↓
NATIVE EXECUTION

É uma diferença técnica importantíssima.


11. AOT — Ahead-of-Time Compilation

Mas também podemos compilar antecipadamente.

Por quê?

A documentação oficial cita razões como:

  • reduzir trabalho durante startup;

  • entregar aplicação sem fontes;

  • gerar classes nomeadas para consumo Java;

  • trabalhar em ambientes onde geração dinâmica de bytecode/classloaders seja indesejada. (Clojure)

A unidade de compilação AOT é um namespace.

A compilação pode produzir vários .class.

A documentação explica inclusive que cada arquivo, função e gen-class pode resultar em classes e que existe uma classe loader __init. (Clojure)

Ou seja:

bellacosa/core.clj
       ↓
AOT
       ↓
bellacosa/core*.class

Agora estamos completamente no território JVM.


12. Então Clojure vira Java?

Não.

Isso é importante.

O fato de gerar JVM bytecode não transforma semanticamente Clojure em Java.

Seria como dizer:

COBOL compilado virou Assembler.

Não.

O target de compilação não define sozinho a linguagem-fonte.

Clojure preserva conceitos como:

persistent data structures
Vars
keywords
symbols
macros
protocols
multimethods
sequences
dynamic typing

mas aproveita a JVM como runtime.


13. JAR: empacotando a criatura

Aplicações JVM frequentemente são distribuídas em:

.jar

Um JAR é essencialmente um arquivo ZIP estruturado contendo:

.class
resources
metadata
manifest

Dependendo da ferramenta de build, você pode produzir um JAR contendo a aplicação e, em algumas estratégias, dependências necessárias.

Então podemos chegar a algo conceitualmente como:

SOURCE
   ↓
BUILD
   ↓
JAR
   ↓
java -jar app.jar

Isso facilita executar Clojure dentro de infraestrutura Java existente.


14. Java interop: aqui o caso fica interessante

Clojure pode chamar Java diretamente.

Exemplo:

(System/currentTimeMillis)

Ou:

(.toUpperCase "cobol")

Resultado:

"COBOL"

Criando objeto:

(java.util.Date.)

Importando:

(import java.time.LocalDate)

Depois:

(LocalDate/now)

Portanto o universo disponível não é apenas:

bibliotecas Clojure

É potencialmente:

Clojure ecosystem
       +
Java ecosystem

Isso muda completamente a análise empresarial.


15. Tipagem dinâmica não significa ausência de tipos

Este erro precisa desaparecer.

Clojure é dinamicamente tipada.

Mas os valores possuem tipos.

(type 42)

não significa que 42 seja uma nuvem metafísica sem representação.

A diferença principal é que você normalmente não declara:

int idade = 42;

como faria em Java.

Você escreve:

(def idade 42)

O tipo está associado ao valor em runtime.

Isso traz flexibilidade.

Também transfere certas classes de erro que linguagens estaticamente tipadas podem detectar durante compilação para testes, runtime, contracts/specs e outras técnicas.


16. nil: o suspeito que sempre volta

Clojure possui:

nil

E apenas:

nil
false

são falsy.

Isto é verdadeiro:

(if 0
  "verdade"
  "falso")

Resultado:

"verdade"

Assim como uma coleção vazia é truthy.

Monk anota imediatamente:

NÃO ASSUMIR
QUE 0 = FALSE.

Boa prática.


17. Keywords: pequenas coisas extremamente importantes

Veja:

:nome

Isso é uma keyword.

Maps usam muito:

{:nome "Adrian Monk"
 :profissao "detetive"
 :saldo 1000M}

Podemos acessar:

(:nome cliente)

Keywords funcionam como funções sobre mapas em muitos casos.

Mais importante em aplicações grandes é usar keywords qualificadas:

:cliente/id
:cliente/nome
:conta/saldo

Isso reduz colisões semânticas.

E se você estudou Datomic, essas formas devem soar muito familiares.

Não é coincidência.


18. Maps são fundamentais

Em Java tradicional você talvez criasse:

class Cliente {
    String nome;
    BigDecimal saldo;
}

Clojure frequentemente trabalha simplesmente com:

{:cliente/nome "Natalie"
 :cliente/saldo 1000M}

Dados permanecem dados.

Não precisamos criar uma classe nova para cada estrutura intermediária.

Isso reduz cerimônia.

Mas também exige disciplina.

Se todo desenvolvedor inventar:

:nome
:Nome
:customer-name
:clientName
:nm_cliente

Monk terá um colapso nervoso.


19. Namespace bem organizado

Um arquivo real:

(ns bellacosa.conta
  (:require
   [clojure.string :as str]))

Depois:

(defn normalizar-nome [nome]
  (str/upper-case nome))

Chamamos:

(normalizar-nome "adrian monk")

Resultado:

"ADRIAN MONK"

Observe:

[clojure.string :as str]

Criamos alias.

Isso permite:

str/upper-case

em vez de repetir:

clojure.string/upper-case

Boa organização de namespaces é uma das primeiras disciplinas que um desenvolvedor Clojure profissional precisa dominar.


20. Convenções importam

Clojure possui convenções próprias.

Funções:

calcular-saldo

em vez de:

calcularSaldo

Predicados frequentemente terminam com ?:

positivo?
valido?
autorizado?

Funções que expressam mutação/efeito significativo frequentemente podem usar ! como convenção:

swap!
reset!
send!

Isso não é decoração.

Ajuda a leitura.

Compare:

(autorizado? cliente)

Você praticamente lê:

autorizado cliente?

Muito Lisp.


21. Não programe Java usando parênteses

Este talvez seja o maior erro de iniciante.

Ele aprende Clojure e tenta recriar:

ClienteClass
ClienteService
ClienteRepository
ClienteFactory
ClienteManager

com Clojure.

Resultado:

Java
vestido de Lisp.

Não faça isso.

Comece perguntando:

Quais são meus dados?

Quais transformações aplico?

Onde realmente existe estado?

Onde existem efeitos colaterais?

Esse é o salto mental.


22. Functional core, imperative shell

Uma excelente disciplina arquitetural é manter grande parte da regra de negócio como funções puras.

Imagine:

(defn aplicar-debito [conta valor]
  (update conta :saldo - valor))

Entrada:

{:saldo 1000M}

Saída:

{:saldo 800M}

Sem:

database
HTTP
Kafka
filesystem
global state

Depois outra camada faz:

LER BANCO
   ↓
FUNÇÃO PURA
   ↓
GRAVAR BANCO

Isso facilita tremendamente testes.


23. Testando

Imagine:

(defn somar [a b]
  (+ a b))

Com clojure.test:

(ns bellacosa.core-test
  (:require
   [clojure.test :refer [deftest is testing]]
   [bellacosa.core :as sut]))

(deftest teste-soma
  (testing "soma dois valores"
    (is (= 30
           (sut/somar 10 20)))))

Observe:

=

verifica igualdade.

E:

is

expressa expectativa.

Funções puras são deliciosamente testáveis:

INPUT
  ↓
FUNCTION
  ↓
EXPECTED OUTPUT

Sem preparar vinte mocks.


24. spec: descrevendo contratos de dados

O Clojure atual inclui como dependências centrais spec.alpha e core.specs.alpha. (Clojure)

A ideia de Spec é permitir descrever propriedades/estruturas dos dados.

Conceitualmente:

(s/def ::saldo decimal?)

Você pode especificar estruturas e usar specs para validação, testes generativos e instrumentação.

Isso é particularmente importante numa linguagem dinâmica.

Mas não cometa o erro oposto:

TIPAGEM DINÂMICA
     ↓
PÂNICO
     ↓
SPEC EM CADA ÁTOMO DO UNIVERSO

Use contratos onde os limites e invariantes realmente importam.


25. Macros: Monk encontra uma passagem secreta

Considere:

(when autorizado?
  (processar))

when é uma macro.

Macros recebem formas de código e produzem outras formas.

Isso é possível de maneira particularmente natural porque código Lisp é representado por estruturas da própria linguagem.

Conceitualmente:

SOURCE CODE
     ↓
DATA STRUCTURE
     ↓
MACRO
     ↓
NEW CODE STRUCTURE
     ↓
COMPILER

É extremamente poderoso.

E perigosíssimo quando usado para mostrar inteligência.

Boa regra:

Se uma função resolve, provavelmente use uma função.

Macros são para quando você realmente precisa transformar a própria estrutura do código.


26. Performance: o caso não termina na JVM

Clojure pode ser rápido.

Mas existem suspeitos conhecidos:

boxing
reflection
lazy sequences
allocation
GC
dynamic dispatch

Em Java interop, por exemplo, type hints podem evitar reflection:

(defn tamanho [^String texto]
  (.length texto))

Mas novamente:

PROFILING FIRST.

Monk concorda.

Você não prende o suspeito antes de encontrar evidências.


27. Lazy sequences: maravilhosas até incendiarem a memória

Clojure utiliza lazy sequences extensivamente.

Exemplo:

(take 10 (range))

range pode representar uma sequência conceitualmente enorme.

Só precisamos dos primeiros dez.

Fantástico.

Mas laziness mal utilizada pode reter referências e aumentar consumo de memória.

Portanto:

LAZY
≠
FREE

O programador precisa entender:

realization
chunking
retained references
allocation

Especialmente em pipelines grandes.


28. Transducers

Quando você evolui, encontrará transducers.

Imagine:

(comp
  (filter even?)
  (map #(* % 10)))

Isso descreve uma transformação independentemente do mecanismo específico que consumirá os dados.

É uma ideia poderosa para composição eficiente de pipelines.

Mas não comece seu primeiro dia tentando dominar transducers.

Monk talvez consiga.

Você não precisa.


29. Protocols e multimethods

Clojure não depende de classes OO para polimorfismo.

Temos recursos como:

protocols
multimethods

Protocols permitem definir conjuntos de operações implementáveis por diferentes tipos.

Multimethods permitem dispatch baseado em funções arbitrárias, indo além do tradicional:

dispatch pelo tipo da classe

Isso oferece polimorfismo sem obrigar a modelagem OO clássica.


30. defrecord

Quando maps simples deixam de ser suficientes para determinadas necessidades de representação/interoperabilidade, existe:

(defrecord Cliente
  [id nome saldo])

Mas novamente:

não transforme todo map em record só porque Java ensinou que toda coisa merece uma classe.

Clojure tende a valorizar estruturas genéricas simples.


31. Tratamento de exceções

Sim, temos:

try
catch
finally
throw

Exemplo:

(try
  (/ 10 0)
  (catch ArithmeticException e
    (println "Monk encontrou o erro.")))

Como estamos na JVM, exceções Java fazem parte do mundo.

Mas regra de negócio normal não deveria virar um festival de exceptions usadas como controle de fluxo.


32. Logging, observabilidade e produção

Uma aplicação Clojure bancária não vive apenas de:

(println "deu ruim")

Você precisa pensar em:

structured logging
metrics
distributed tracing
correlation IDs
latency
JVM metrics
heap
GC
thread pools
HTTP metrics
database metrics
Kafka lag

A linguagem pode ser funcional.

Produção continua sendo produção.

Monk pergunta:

— Quem alterou esse saldo?

Se seus logs responderem:

não sabemos

o paradigma funcional não salvará ninguém.


33. O que um COBOLzeiro já sabe e talvez não perceba

Existe muita bagagem transferível.

COBOL

DATA DIVISION

Você entende importância da estrutura dos dados.

Clojure

maps
vectors
sets
EDN

Dados continuam centrais.


COBOL

Você entende:

batch
transação
integridade
restart

Clojure

Precisará aplicar isso em:

services
events
Kafka
APIs
Datomic

COBOL

Você conhece:

COPYBOOK

Clojure

Você precisará entender:

namespaces
libraries
contracts
data schemas

Não são equivalentes.

Mas a disciplina mental viaja bem.


34. Normas e padrões: existe um ANSI Clojure?

Aqui encontramos uma diferença enorme.

COBOL possui longa história de padronização formal, incluindo padrões ANSI/ISO.

Clojure não é definida por um grande padrão ISO equivalente.

A referência prática da linguagem é formada pela implementação oficial, documentação, reference pages, API e processo de evolução mantido pelo core team.

O desenvolvimento é deliberadamente conservador, com forte preocupação com compatibilidade retroativa. Atualmente o core é desenvolvido por uma equipe no Nubank, que apoia esse trabalho. (Clojure)

Isso significa:

COBOL

ISO / ANSI
   ↓
vendors
   ↓
implementations

versus, simplificando:

CLOJURE

canonical implementation
       +
official reference
       +
core team

É outro modelo de governança.


35. Compatibilidade é uma obsessão curiosamente mainframe

Aqui Clojure surpreende.

A equipe oficial enfatiza evolução cuidadosa e backward compatibility. (Clojure)

Para uma linguagem associada a startups e fintechs, isso soa quase... mainframe.

O COBOLzeiro pergunta:

— Então eles evitam quebrar código antigo?

— Sim.

— Mudam a linguagem com cautela?

— Sim.

— Valorizam estabilidade?

— Sim.

— Tem certeza de que isso não foi criado em Poughkeepsie?

😂


36. GitHub e contribuição: outra peculiaridade

O código oficial está no repositório Clojure no GitHub e é distribuído sob Eclipse Public License 1.0. (GitHub)

Mas o desenvolvimento do core não funciona simplesmente como:

fork
↓
PR aleatório
↓
merge

Existe um processo próprio de contribuição, discussão e avaliação.

E há uma curiosidade muito atual: as regras oficiais de contribuição dizem explicitamente que código gerado por LLMs não é aceito como contribuição para o core por incompatibilidade com os compromissos do Contributor Agreement, salvo situações específicas envolvendo geradores escritos por humanos. (Clojure)

Isso é um belo Easter egg de 2026:

Você pode pedir a uma IA que explique Clojure.

Mas não pode simplesmente mandar o código dela para dentro do Clojure core.

Monk aprova.

— Precisamos saber exatamente quem escreveu cada linha.


37. O checklist Monk para um novo desenvolvedor Clojure

Antes de alguém se declarar desenvolvedor Clojure, eu gostaria que soubesse pelo menos:

  • Reader, forms e evaluation; namespaces e organização física dos arquivos; collections, sequences, maps e keywords; def, defn, let, if, cond, case, map, filter e reduce; funções de ordem superior, imutabilidade e persistent data structures; REPL-driven development; atoms, refs e noções de concorrência; Java interop e JVM; deps.edn, classpath e dependências; testes com clojure.test; tratamento de exceções; lazy sequences; macros sem abusar delas; profiling, heap e GC; e, acima de tudo, a diferença entre valor, identidade, estado e tempo.

Essa última linha é a que prepara o caminho para Datomic.


38. A comparação técnica final

CaracterísticaCOBOLClojure
Origem19592007
Paradigma dominanteImperativo/proceduralFuncional
TipagemEstáticaDinâmica
EstruturaDivisions/Sections/ParagraphsForms/namespaces/functions
DadosRecords/copybooksMaps/vectors/sets
MutabilidadeNormalDesencorajada
CompilaçãoCompilador nativo/mainframeJVM bytecode
RuntimeLE/z/OS etc.JVM
InteratividadeTradicionalmente baixaREPL central
ConcorrênciaPlataforma/runtimeRecursos JVM + Clojure
MetaprogramaçãoLimitadaMacros Lisp
DependênciasMainframe toolingdeps.edn/Maven ecosystem
PadronizaçãoANSI/ISOImplementação/referência oficial
EcossistemaMainframe empresarialJVM/funcional
InteropCICS, Db2, IMS, LE etc.Java/JVM
FilosofiaProcedimentos sobre registrosTransformações sobre valores

39. Monk encontra o verdadeiro defeito

São 18h42.

O programa está funcionando.

Testes verdes.

REPL funcionando.

Heap estável.

GC normal.

Namespaces organizados.

Monk continua olhando para o código.

Natalie pergunta:

— O que foi agora?

Ele aponta:

(defn ProcessarCliente [Cliente]
  ...)

Silêncio.

— Mr. Monk?

— Maiúsculas.

— Funciona.

— Eu sei.

— Então deixe.

— Não posso.

Ele corrige:

(defn processar-cliente [cliente]
  ...)

Respira profundamente.

— Agora podemos colocar em produção.


☕ Epílogo — O COBOLzeiro fecha o caso

Existe uma tentação perigosa quando encontramos Clojure pela primeira vez.

Olhar:

(->> clientes
     (filter ativo?)
     (map calcular-risco)
     (sort-by :score))

e concluir:

“Isso é simples demais para aplicações bancárias sérias.”

Mas simplicidade sintática não significa simplicidade computacional.

Por baixo daquele pequeno programa podem existir:

Clojure Reader
      ↓
macro expansion
      ↓
compiler
      ↓
JVM bytecode
      ↓
classloader
      ↓
HotSpot JIT
      ↓
garbage collector
      ↓
threads
      ↓
network
      ↓
Kafka
      ↓
Datomic
      ↓
AWS

É uma pilha tecnológica sofisticada.

O COBOL também sofre do preconceito inverso.

Alguém vê:

MOVE A TO B

e pensa:

“Linguagem primitiva.”

Até descobrir que aquele MOVE está dentro de um programa CICS conectado a Db2, MQ, RACF, Parallel Sysplex, WLM, SMF e bilhões de dólares de operações.

Nunca julgue uma arquitetura pelo tamanho do verbo.

Clojure ensina algo especialmente interessante ao programador COBOL.

Apesar de separados por quase cinquenta anos, ambos valorizam dados de maneira muito explícita.

COBOL colocou isso literalmente numa:

DATA DIVISION

Clojure coloca dados no centro usando:

{:cliente/id 123
 :cliente/nome "Adrian Monk"
 :cliente/saldo 1000M}

A diferença fundamental aparece quando chega a mudança.

COBOL pergunta:

COMO ALTERO O ESTADO?

Clojure frequentemente pergunta antes:

PRECISO REALMENTE ALTERAR ESTE ESTADO?

Essa pergunta conduz à imutabilidade.

A imutabilidade conduz a uma abordagem diferente de concorrência.

Essa abordagem conduz naturalmente às ideias de valores ao longo do tempo.

E quando seguimos esse túnel suficientemente longe...

encontramos novamente:

DATOMIC

É por isso que estudar Clojure antes de tentar compreender profundamente Datomic é tão revelador.

Você finalmente percebe que Datomic não apareceu de um disco voador.

Existe uma linha intelectual:

LISP
  ↓
CLOJURE
  ↓
VALUES
  ↓
IMMUTABILITY
  ↓
IDENTITY ≠ VALUE
  ↓
TIME
  ↓
DATOMIC

E isso explica também parte da história tecnológica do Nubank.

Não escolheram simplesmente uma linguagem cheia de parênteses e um banco estranho.

Adotaram uma família coerente de ideias sobre como controlar complexidade, estado, concorrência e tempo.

Se essas escolhas servem para qualquer empresa é outra investigação.

E Monk jamais encerraria um caso sem fazer essa pergunta.

Antes de aprovar Clojure para seu próximo sistema, ele alinharia cuidadosamente três papéis sobre a mesa:

PROBLEMA

ARQUITETURA

TECNOLOGIA

Depois verificaria se estão nessa ordem.

Porque se você começar pela tecnologia e sair procurando um problema para justificá-la...

há algo errado.

Não sabemos ainda o quê.

Mas Monk sabe.

É uma bênção... e uma maldição.

☕ Fim do caso.

Para continuar tecnicamente, as melhores portas são a Referência oficial do Clojure, o guia oficial de aprendizado e a documentação específica sobre compilação e AOT.

sexta-feira, 16 de agosto de 2024

House M.D., COBOL e o Caso do Banco de Dados que se Recusava a Esquecer: Datomic versus Db2 na Sala de Diagnóstico

 

Bellacosa Mainframe e a comparacao entre datomic e db2

☕ Um Café no Bellacosa Mainframe

House M.D., COBOL e o Caso do Banco de Dados que se Recusava a Esquecer: Datomic versus Db2 na Sala de Diagnóstico

🩺 SQL contra Datalog, UPDATE contra imutabilidade, buffer pools contra caches, logs contra história nativa, scale-up contra scale-out, décadas de cicatrizes contra arquitetura funcional — e a pergunta que ninguém deveria responder com religião tecnológica: “qual deles eu usaria para guardar o dinheiro?”

House entra na sala.

Olha para o quadro.

De um lado:

IBM Db2

Do outro:

Datomic

Foreman pergunta:

— Qual é o diagnóstico?

House apoia a bengala na mesa.

— Um deles tem quase meio século de experiência em ambientes corporativos. O outro nasceu em 2012 de um cara que achava que mudar estado demais era uma péssima ideia.

Cameron:

— Então Db2 ganha?

House:

— Você não entendeu nada.

Chase:

— Datomic ganha?

House:

— Você também não.

Ele olha para todos.

— Banco de dados não é concurso de popularidade. É diagnóstico diferencial.

E aí começa nossa consulta.

Porque comparar Datomic com Db2 apenas perguntando “qual é melhor?” é como perguntar se um bisturi é melhor que uma ressonância magnética.

Depende do problema.

Depende do paciente.

Depende do risco.

E, principalmente:

depende do que você está tentando preservar, consultar, transacionar e recuperar quando tudo der errado às 03h17 da manhã.

Pegue o café.

Hoje vamos abrir o paciente.


1. Primeiro sintoma: ambos guardam dados, mas pensam diferente

Db2 nasceu dentro da tradição relacional.

Você pensa em:

TABLE
ROW
COLUMN
PRIMARY KEY
FOREIGN KEY
INDEX
SQL

Exemplo:

CREATE TABLE CONTA (
    ID        CHAR(10) NOT NULL,
    TITULAR   VARCHAR(100),
    SALDO     DECIMAL(15,2),
    PRIMARY KEY (ID)
);

Depois:

SELECT SALDO
FROM CONTA
WHERE ID = '000123';

Simples.

Familiar.

Décadas de ferramentas, otimizadores, DBAs, utilities, backups, logs, reorgs, statistics, access paths e trauma acumulado.

Datomic pensa diferente.

Ele enxerga fatos.

Conceitualmente:

ENTITY
ATTRIBUTE
VALUE
TRANSACTION

Algo como:

[1001 :conta/id "000123"]
[1001 :conta/titular "Arthur Dent"]
[1001 :conta/saldo 1000]

Então já aparece a primeira diferença fundamental:

Db2

“Tenho registros relacionais.”

Datomic

“Tenho fatos sobre entidades ao longo do tempo.”

House olha para o quadro.

— Não são duas implementações do mesmo modelo.

— Então são doenças diferentes?

— Não. São anatomias diferentes.


2. O grande choque: UPDATE

Aqui começa a parte que faz o COBOLzeiro apertar a caneca.

Em Db2:

UPDATE CONTA
   SET SALDO = 800
 WHERE ID = '000123';

Estado anterior:

SALDO = 1000

Estado atual:

SALDO = 800

Claro: Db2 possui transaction log, temporal tables, auditing, CDC e recursos para reconstruir histórico.

Mas o modelo lógico mais comum continua sendo:

ANTES
  ↓
UPDATE
  ↓
DEPOIS

Datomic trata a evolução do dado de forma diferente.

O histórico faz parte do modelo.

Conceitualmente:

T1
conta/saldo = 1000

T2
retract saldo 1000
assert saldo 800

Você não simplesmente “apagou” a existência anterior.

Você criou uma nova realidade temporal.

House aponta:

— Um sofre amnésia por padrão lógico.

Wilson responde:

— Db2 não perde o passado, House.

— Eu disse lógico, não físico. Preste atenção.

Essa distinção é importante.


3. Db2 tem logs. Datomic tem memória histórica como filosofia

Db2 registra transações para recovery.

Isso é central.

BEGIN
  UPDATE
  UPDATE
COMMIT

Db2 precisa saber como:

ROLLBACK
REDO
UNDO
RECOVER

O log é infraestrutura de sobrevivência.

Datomic trata a história como parte natural do database value.

Você pode consultar algo como:

as-of
since
history

Ou seja:

QUAL ERA O BANCO
EM T1?

não é uma pergunta excepcional.

É parte natural da arquitetura.

Aqui está uma diferença conceitual enorme.

Db2:

histórico pode ser construído, configurado, auditado e recuperado.

Datomic:

histórico está no DNA.

Nenhum dos dois é “mais sério” automaticamente.

Mas o segundo torna alguns problemas temporais muito mais naturais.


4. Diagnóstico diferencial: SQL versus Datalog

Db2 fala SQL.

E SQL é provavelmente uma das linguagens técnicas mais difundidas da história.

SELECT C.NOME,
       SUM(M.VALOR)
FROM CLIENTE C
JOIN MOVIMENTO M
  ON M.CLIENTE_ID = C.ID
WHERE C.STATUS = 'ATIVO'
GROUP BY C.NOME;

Milhões de pessoas entendem isso.

Datomic usa Datalog.

Exemplo conceitual:

[:find ?nome (sum ?valor)
 :where
 [?c :cliente/nome ?nome]
 [?c :cliente/status :ativo]
 [?m :movimento/cliente ?c]
 [?m :movimento/valor ?valor]]

O modelo é declarativo também.

Mas pensa mais em relações lógicas.

House olha.

— Isso é SQL depois de passar três semanas num retiro filosófico?

Cameron:

— É Datalog.

— Exatamente o que eu disse.

😂


5. Db2 tem tabelas. Datomic tem entidades e atributos

No mundo relacional:

CLIENTE
  |
  +-- ID
  +-- NOME
  +-- CPF
  +-- STATUS

No Datomic:

ENTITY 1001
  |
  +-- :cliente/id
  +-- :cliente/nome
  +-- :cliente/cpf
  +-- :cliente/status

Isso pode parecer semanticamente parecido.

Mas não é idêntico.

No Db2, schema é fortemente estruturado em tabelas.

No Datomic, atributos são entidades do próprio schema.

A flexibilidade muda.

E também muda a maneira de evoluir schema.

Em Datomic existe uma tendência forte a schema aditivo:

adiciona atributo
adiciona relação
preserva passado

No Db2 você pode fazer:

ALTER TABLE

com todos os cuidados que conhece.

O ponto é:

ambos possuem schema, mas a filosofia de evolução é diferente.


6. Índices: Db2 pergunta “qual index?”. Datomic responde “qual ordenação?”

No Db2:

TABLE
  |
  +-- INDEX A
  +-- INDEX B
  +-- INDEX C

Você escolhe índices conforme acesso.

O DBA pensa em:

CLUSTERING
CARDINALITY
SELECTIVITY
INDEX ONLY
MATCHING COLUMNS
ACCESS PATH

Datomic organiza datoms em índices como:

EAVT
AEVT
AVET
VAET

Onde:

E = Entity
A = Attribute
V = Value
T = Transaction

Para um COBOLzeiro com VSAM no DNA, isso é surpreendentemente compreensível.

Você pergunta:

— Como chego ao dado?

Resposta:

— Escolha a ordenação adequada.

Não é VSAM.

Mas seu cérebro reconhece o cheiro de índice.


7. Buffer pool versus cache: dois mundos, mesma física

Agora entramos numa comparação deliciosa.

No Db2, buffer pools são críticos.

DISK
  ↓
BUFFERPOOL
  ↓
SQL

Quanto mais páginas úteis permanecem em memória:

menos I/O
mais performance

Você pensa em:

GETPAGE
HIT RATIO
PAGESET
BUFFER POOL SIZE
PREFETCH

No Datomic:

STORAGE
  ↓
CACHE
  ↓
PEER / COMPUTE
  ↓
QUERY

A diferença é que segmentos imutáveis são particularmente amigáveis a cache.

Se um pedaço histórico não muda, ele não sofre o mesmo problema clássico de invalidação.

House:

— Então descobriram que dados que não mudam são fáceis de cachear?

Foreman:

— Simplificando, sim.

— Nobel amanhã.

😂

Mas tecnicamente é uma vantagem real.


8. Working set: aqui os dois se encontram

Essa é uma das partes mais importantes.

Imagine:

DATABASE = 20 TB
WORKING SET = 10 GB

e você possui cache/memória suficiente.

Pode funcionar muito bem.

Agora:

DATABASE = 2 TB
WORKING SET = 1,5 TB

e sua memória útil é 64 GB.

Problema.

Isso vale tanto para arquiteturas relacionais quanto para Datomic.

O tamanho total do banco não é a única pergunta.

A pergunta é:

quanto do dado útil precisa estar quente ao mesmo tempo?

O velho DBA e o engenheiro Clojure podem brigar sobre filosofia.

A física ignora os dois.


9. CPU: Db2 e Datomic gastam por razões diferentes

Db2 usa CPU em:

SQL execution
sort
join
predicate evaluation
index access
locking
logging
utilities
compression

Datomic usa CPU em:

queries
Datalog
transaction processing
indexing
caching
application-side computation
GC

E aqui aparece uma diferença importante:

Datomic vive no ecossistema JVM.

Então:

GARBAGE COLLECTION

entra no prontuário.

O COBOLzeiro:

— Finalmente achei o tumor.

House:

— Não. Só um sintoma.


10. Transações: ambos levam ACID a sério

Db2:

BEGIN
UPDATE
INSERT
COMMIT

ou:

ROLLBACK

Transação é parte central da arquitetura.

Datomic também oferece transações ACID.

Mas sua coordenação de escrita é arquiteturalmente distinta.

No Datomic Pro clássico:

WRITES
  ↓
TRANSACTOR
  ↓
STORAGE

Leituras podem ser distribuídas através de peers.

Isso cria um trade-off interessante:

READ SCALE
↑

muito bem horizontalizado.

Mas writes possuem uma coordenação ordenada.

O veterano imediatamente pergunta:

qual é o throughput máximo de escrita por database?

Essa é uma pergunta correta.


11. Db2 escala diferente

Db2 historicamente escala de várias maneiras.

No mainframe:

Db2
  |
DATA SHARING
  |
Parallel Sysplex

Você pode ter múltiplos members compartilhando dados com locking e buffer management distribuído.

Além disso:

partitioning
parallelism
zIIP
buffer pools
workload management

A filosofia é muito mais:

um sistema transacional extremamente poderoso e coordenado.

Datomic tende mais a:

muitos databases/domínios, reads distribuídos, facts imutáveis e sharding arquitetural.

Nenhuma solução é “mais moderna” por definição.

São estratégias diferentes.


12. Escala: uma base monstruosa ou vários domínios?

No mundo tradicional, é natural encontrar:

PRODUCTION DB2
████████████████████████████████
muitos sistemas
muitas tabelas
muitos anos
████████████████████████████████

Datomic, especialmente em arquiteturas de microservices, incentiva uma decomposição maior:

SERVICE A → DATABASE A
SERVICE B → DATABASE B
SERVICE C → DATABASE C
SERVICE D → DATABASE D

Isso ajuda a explicar números como dezenas de milhares de databases em uma empresa como Nubank.

Mas traz outra complexidade:

distributed consistency
cross-service workflows
events
reconciliation
ownership
observability

House escreve no quadro:

MONOLITO:
complexidade interna

MICROSERVIÇOS:
complexidade distribuída

— Escolha onde quer sofrer.


13. Recovery: aqui o velho médico fica sério

Db2 possui uma história monumental de recuperação.

Você tem:

logs
image copies
RECOVER
REORG
RUNSTATS
utilities
PIT recovery
system-level backup
data sharing recovery

Décadas de prática.

Datomic também possui mecanismos de durabilidade, HA, backups e reconstrução, mas o ecossistema operacional é diferente e muito menor.

Se você perguntar:

“Quem conhece todos os detalhes de recuperação do Db2 em produção?”

há um mercado gigantesco de experiência.

Para Datomic:

o universo é muito menor.

Isso é risco.

Não necessariamente falha da tecnologia.

Mas risco de skills e tooling.


14. Operação: o que acontece às 03:17?

Essa talvez seja a diferença mais subestimada.

Db2 tem:

DBA
sysprog
vendor support
utilities
APARs
PTFs
Redbooks
manuals
decades of incidents

Datomic tem:

smaller community
Clojure expertise
specialized knowledge
fewer battle-tested operators

Hoje possui produção gigantesca em Nubank.

Mas não possui a mesma ubiquidade operacional.

House:

— O remédio novo pode ser excelente.

Wilson:

— Mas?

— Se ninguém no hospital sabe dosar, administrar ou tratar efeitos colaterais, isso também entra no diagnóstico.

Exatamente.


15. Skill gap versus obscuridade

Db2:

difícil achar bons especialistas?
sim

mas existem muitos.

Datomic:

especialistas?
existem

mercado?
pequeno

Agora coloque Clojure junto.

Seu pool de recrutamento diminui ainda mais.

Isso pode ser aceitável para uma empresa que constrói cultura própria, treina pessoas e domina a stack.

Pode ser um desastre para uma empresa que terceiriza tudo e troca consultoria a cada dois anos.

Essa é uma consideração organizacional, não apenas técnica.


16. Vendor lock-in: os dois têm, mas de formas diferentes

Db2:

IBM ecosystem
z/OS integration
utilities
features
licensing
skills

Se você explora profundamente recursos específicos, migrar pode ser caro.

Datomic:

Datomic model
Datalog
Clojure ecosystem
API model
temporal semantics

Também cria dependência.

A diferença é que no caso Datomic o ecossistema é menor.

Mas há uma peculiaridade histórica:

Nubank acabou adquirindo Cognitect.

Então:

usuário
  ↓
produto
  ↓
fabricante

vira

usuário + mantenedor

Isso reduz um risco para Nubank.

Não necessariamente para todos.


17. Histórico e auditoria: Datomic brilha

Se seu domínio vive perguntando:

qual era o estado?
quando mudou?
quem introduziu?
qual sequência produziu isso?

Datomic possui uma elegância impressionante.

Isso pode ser ouro em:

fraude
compliance
investigação
reprodução de estado
auditoria
reprocessamento

Db2 faz isso muito bem com ferramentas e recursos adequados.

Mas frequentemente você precisa arquitetar mais explicitamente:

temporal tables
audit columns
CDC
history tables
logs
triggers

Datomic coloca o tempo no centro.

Essa é provavelmente uma das maiores razões para escolhê-lo.


18. Mas histórico custa storage

Datomic preserva fatos.

Além disso, datoms aparecem em múltiplas ordenações de índice.

Então:

mais história
+
mais índices
=
mais storage

Pode haver compressão e atributos noHistory, mas a filosofia continua.

Db2 pode ser mais econômico se o requisito for apenas estado atual e histórico limitado.

Então:

Datomic

“O passado é precioso.”

Db2

“Diga quanto passado você quer e eu configuro.”

House:

— Um é historiador.

— E o outro?

— Contador.


19. Throughput de escrita: aqui Datomic exige respeito

Se seu sistema é:

READS >>> WRITES

Datomic pode se encaixar muito bem.

Se seu problema é:

WRITE FIREHOSE

você precisa medir cuidadosamente.

Porque a serialização transacional por database e background indexing criam limites naturais.

Se a taxa de entrada ultrapassa a capacidade de indexação:

MEMORY INDEX cresce
   ↓
back pressure
   ↓
write throughput reduz

Db2 também possui limites, obviamente.

Locks.

Log throughput.

I/O.

Latches.

Buffer contention.

Mas sua arquitetura e histórico de workloads OLTP de escrita extrema são muito conhecidos.

Para uma central bancária gigantesca, isso pesa.


20. Query analítica: nenhum dos dois substitui tudo

Datomic não deveria virar:

data lake
warehouse
log bucket
BI universal

Da mesma forma, Db2 OLTP não deveria necessariamente ser martelado com qualquer analytics gigantesco sem arquitetura adequada.

No mundo moderno:

SYSTEM OF RECORD
   ↓
CDC/EVENTS
   ↓
ANALYTICS PLATFORM

é frequentemente melhor.

House:

— Então não coloque tudo no mesmo banco?

— Exato.

— Finalmente uma ideia saudável.


21. Mainframe versus cloud: não confunda database com plataforma

Db2 pode existir em várias plataformas, mas para nosso universo Bellacosa, pense em Db2 for z/OS.

Aí você ganha:

IBM Z
z/OS
WLM
Sysplex
RACF
zIIP
SMF
RMF
CICS
IMS
MQ

O database vive dentro de uma infraestrutura extremamente integrada.

Datomic normalmente vive numa arquitetura distribuída/cloud-native:

AWS
compute
storage
DynamoDB/S3 em Datomic Cloud
Clojure
services
Kafka
Kubernetes

Então parte da comparação não é:

Datomic vs Db2

É:

ARQUITETURA DISTRIBUÍDA
vs
PLATAFORMA INTEGRADA

Isso muda custo, skills, observabilidade, DR e operação.


22. Segurança: Db2 herda um castelo

Db2 for z/OS vive num ambiente com:

RACF
SAF
audit
SMF
encryption
TLS
dataset protection
roles
privileges

É um ecossistema de segurança brutalmente maduro.

Datomic depende de sua arquitetura e da cloud:

IAM
network policies
KMS
secrets
AWS controls
application authorization

Ambos podem ser seguros.

Mas segurança no mainframe tende a ser altamente centralizada e conhecida.

No cloud-native, a superfície é mais distribuída.

Mais flexível.

E também mais fácil de configurar errado.


23. Observabilidade: SMF versus mundo distribuído

Db2:

SMF
RMF
IFCID
statistics
accounting traces
performance monitors

Você consegue investigar profundamente.

Datomic + microservices:

metrics
logs
traces
OpenTelemetry
application telemetry
AWS metrics
JVM metrics
GC metrics

O problema moderno é que uma transação pode atravessar dez serviços.

Então observability distribuída vira essencial.

O velho DBA olha para isso e diz:

— Vocês desmontaram o sistema e agora precisam descobrir onde cada pedaço está.

😂

É maldade.

Mas contém um pouco de verdade.


24. Latência: proximidade importa

Db2 for z/OS próximo de CICS:

CICS
 ↓
COBOL
 ↓
Db2

latência muito baixa e altamente previsível dentro da plataforma.

Datomic pode ganhar em leituras locais através de caches/peers.

Mas numa arquitetura cloud existem mais possibilidades de:

network hop
service hop
timeout
retry

Em compensação, você pode escalar serviços independentemente.

Trade-off.

Sempre trade-off.


25. Consistência distribuída: Datomic simplifica algumas coisas e complica outras

Dentro de um database Datomic, consistência é forte e transações são ordenadas.

Mas se você possui:

DB A
DB B
DB C

e um workflow precisa alterar todos:

agora você tem uma questão distribuída.

Pode usar:

events
sagas
reconciliation
idempotency

O velho mainframe pensa:

“Então vocês tiraram a complexidade do banco e colocaram na aplicação?”

Às vezes, sim.

Não por incompetência.

Por escolha arquitetural.


26. Db2 também não é mágico

Agora vamos bater no outro paciente.

Db2 pode virar:

monstro

com:

milhares de tabelas
milhares de índices
stored procedures
triggers
views
packages
utilities
bindings
access paths
partitions

E se ninguém cuida:

RUNSTATS velho
REBIND errado
buffer mal dimensionado
index demais
SQL ruim
locking
tablespace gigante

a criatura degrada.

House:

— Tecnologia madura não cura arquitetura ruim.

Exatamente.


27. Por que eu escolheria Db2?

Se eu tivesse:

core financeiro crítico
alto volume OLTP
equipe mainframe madura
CICS
COBOL
IMS/MQ
necessidade de SQL
forte integração z/OS
ferramentas robustas
auditoria consolidada

Db2 seria uma escolha extremamente natural.

Principalmente quando:

risk tolerance = LOW

e:

operational predictability = VERY HIGH

Você compra não apenas database.

Compra décadas de ecossistema.


28. Por que eu escolheria Datomic?

Se eu tivesse:

greenfield
Clojure
domínio temporal forte
histórico essencial
muitos reads
arquitetura distribuída
microservices
preciso reconstruir estados

Datomic poderia ser extremamente atraente.

Especialmente se minha organização estivesse disposta a dominar:

Clojure
Datalog
capacity model
sharding
observability
cloud operations

Ele pode reduzir complexidade em problemas históricos/temporais que seriam mais trabalhosos em bancos tradicionais.


29. O maior erro: escolher Datomic porque Nubank usa

House escreve:

NUBANK USES IT
≠
YOU SHOULD USE IT

Isso vale para qualquer tecnologia.

Netflix usa algo?

Legal.

Google usa algo?

Interessante.

Banco X usa algo?

Ótimo.

Pergunta:

seu problema é igual?

Provavelmente não.

Arquitetura de referência não é receita de bolo.


30. O segundo maior erro: escolher Db2 só porque “sempre usamos”

Agora House vira para o outro lado.

WE ALWAYS USED DB2
≠
DB2 IS BEST HERE

Se o sistema é novo, isolado, temporal, distribuído e possui requisitos diferentes, talvez Db2 seja exagero ou inadequado economicamente.

Legado pode ser estabilidade.

Mas também pode virar automatismo.

O bom arquiteto pergunta:

por que estamos escolhendo isso?

Não:

“qual tecnologia eu já conheço?”


31. Comparativo direto

TemaDb2Datomic
ModeloRelacionalFatos imutáveis
QuerySQLDatalog
AtualizaçãoUPDATEassert/retract
HistóricoRecursos específicos/logs/temporalNativo ao modelo
TransaçõesACIDACID
EscritasOLTP altamente maduroSerializadas por database
LeiturasMuito escalável, inclusive Data SharingPeers/compute horizontais
CacheBuffer poolsSegments/caches imutáveis
ÍndicesDefinidos pelo schema/workloadEAVT/AEVT/AVET/VAET
EcossistemaGigantescoPequeno
SkillsAmplamente disponíveisNicho
ToolingExtremamente maduroEspecializado
HA/DRDécadas de soluçõesDisponível, arquitetura diferente
Cloud fithíbrido/múltiplas opçõesforte
Mainframe fitnativo em z/OSnão
Temporalidadepoderosa, mas configuradacentral
Vendor riskIBMecossistema Datomic/Nubank
Greenfielddependeforte candidato
Core bancário legadoexcelente fitmigração não trivial

32. Easter egg: House faz o teste do saldo

House escreve:

SALDO INICIAL = 1000

Depois:

COMPRA = 200

Pergunta:

— Db2?

UPDATE CONTA
SET SALDO = 800;

— Datomic?

assert novo saldo 800
retract saldo 1000

House:

— Qual está certo?

Foreman:

— Ambos.

— Finalmente.

Depois:

— Qual consegue dizer que o saldo foi 1000 antes?

Cameron:

— Ambos podem, se o Db2 estiver adequadamente configurado.

House sorri.

— Agora você está aprendendo.


33. O verdadeiro teste não é performance

É falha.

Pergunte:

O que acontece se:
  • storage ficar lento?

  • rede quebrar?

  • processo morrer?

  • região cloud falhar?

  • transaction log encher?

  • buffer pool degringolar?

  • indexing atrasar?

  • GC congelar?

  • deploy inserir bug?

  • schema mudar errado?

  • restore falhar?

  • DR não estiver testado?

Tecnologia não se prova em benchmark.

Prova-se:

quando o mundo fica feio.


34. O diagnóstico final de House

House olha para o quadro.

De um lado:

Db2

Do outro:

Datomic

Ele apaga a palavra:

VERSUS

e escreve:

FOR WHAT?

Porque esse é o diagnóstico.

Db2 não é “velho demais”.

Datomic não é “novo demais”.

Db2 traz:

maturidade
ecossistema
ferramentas
performance OLTP
integração z/OS
skills
previsibilidade

Datomic traz:

imutabilidade
tempo
fatos
histórico
read scaling
arquitetura funcional
modelagem diferente

São propostas distintas.


☕ Epílogo — O paciente sobrevive

São 03h42.

Produção liga.

Foreman atende.

— Temos problema.

House:

— Db2?

— Não.

— Datomic?

— Também não.

— Então?

— A aplicação cobrou o cliente duas vezes.

House olha para o time.

— Infrastructure?

— Tudo verde.

— Database?

— Consistente.

— Logs?

— Perfeitos.

— Então quem errou?

Silêncio.

House aponta para o quadro:

APPLICATION LOGIC

— Everybody lies.

Incluindo código.

O COBOLzeiro no canto toma café.

Porque essa é a verdade que atravessa VSAM, IMS, Adabas, Db2, Datomic, Oracle, PostgreSQL e qualquer banco que ainda inventaremos:

um database pode armazenar perfeitamente a coisa errada.

No final, a pergunta nunca foi:

“Qual database é melhor?”

É:

“Qual arquitetura torna mais difícil errar, mais fácil detectar quando erramos e mais seguro recuperar quando inevitavelmente errarmos?”

Para um core bancário tradicional fortemente integrado ao IBM Z, com décadas de lógica COBOL/CICS e requisitos de altíssimo throughput transacional, Db2 continua sendo uma escolha absurdamente forte.

Para um sistema greenfield, distribuído, fortemente temporal, Clojure-native, com histórico e reconstrução de estado no centro do problema, Datomic pode oferecer uma elegância que o modelo relacional não entrega da mesma maneira sem bastante engenharia adicional.

Mas se alguém entrar na reunião dizendo:

“Vamos usar Datomic porque Nubank usa.”

House deveria expulsá-lo.

E se outro disser:

“Vamos usar Db2 porque sempre usamos.”

House também.

Depois perguntaria:

QUAL É O PROBLEMA?

Só então escolheria o tratamento.

Porque banco de dados, assim como medicina, não é sobre preferência pessoal.

É sobre diagnóstico correto, efeitos colaterais conhecidos e o paciente continuar vivo depois do deploy.

☕ Fim do café.

Vagner Renato Bellacosa, IBM Champion e especialista em IBM Mainframe
IBM Z17 SYSTEM ONLINE
Sobre o autor

Vagner Renato Bellacosa

IBM Champion 2026 • Especialista em IBM Mainframe

Vagner Renato Bellacosa trabalha com IBM Mainframe desde 1988 , é IBM Champion e especialista em COBOL, CICS, Db2, z/OS e IBM Z. Compartilha experiências profissionais, conhecimento técnico, história da computação e práticas do universo mainframe para aproximar novas gerações das tecnologias que sustentam empresas, bancos e governos ao redor do mundo.

IBM Champion IBM Z COBOL CICS Db2 z/OS Mainframe desde 1988
GitHub LinkedIn
Inicializando conteúdo...