Pular para o conteúdo
2 min de leitura

ULID em coluna UUID: a interop que evitou uma migração inteira

Por Equipe Tech do Sonne ·

Como armazenar ULIDs em colunas UUID sem perder funcionalidade nem performance.

Camverly usa ULID como identificador em todas as entidades. Mas Postgres não tem tipo nativo ULID, e usar TEXT pra ID destrói indexação. A solução que adotamos: armazenar ULID em coluna UUID. Os dois tipos têm 16 bytes; a interop é gratuita se você faz a conversão certa. Levou dois bugs em produção pra descobrir os pontos onde isso vaza, mas hoje é tema fechado.

ULID é especificado como 16 bytes: 6 bytes de timestamp seguidos de 10 bytes randômicos. UUID v4 é também 16 bytes mas com layout diferente (variant + version bits embutidos). Como representação string, ULID usa Crockford Base32 (26 chars), UUID usa hex com hífens (36 chars). O truque é: como nunca decodificamos o UUID pra extrair version/variant, o layout interno não importa. Os 16 bytes podem ser interpretados como ULID ou UUID indiferentemente.

O código que faz a ponte é pequeno e mora num arquivo uuid_helper.go em cada package de persistência: uuidStr(u ulid.ULID) string converte os bytes do ULID pra formato UUID hex com hífens. parseAnyULID(s string) aceita tanto 26-char Crockford quanto 36-char UUID hex, retornando ulid.ULID. Esses dois helpers estão em cada repository, e toda interação com o banco passa por eles.

Onde dói: APIs externas e contratos REST. Se você expõe IDs como JSON, qual formato escolhe? Nós optamos por aceitar os dois na entrada (parseID() aceita ambos), mas serializar saída como UUID hex 36-char. Razão: clientes que consomem da gateway provavelmente vão escrever direto em banco ou comparar com colunas UUID, então UUID hex é o formato amigável. ULID Crockford fica como representação interna do Go.

O bug que apareceu em produção: o outbox publisher publicava `aggregate_id` como Crockford 26-char num campo UUID. Postgres aceita string '01HXX...' como UUID? Não. Erro 'invalid input syntax for type uuid'. A correção foi sed-style — substituir todas chamadas `.String()` por `uuidStr()` nos repositórios. Treze repos atingidos. Hoje a regra de ouro é: nunca chame .String() de ULID em contexto de banco. Existe gosec lint custom rodando no CI que detecta isso e marca como erro.

Leituras relacionadas

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly