O que vocês acham de usar uma Check Constraint para criar um NOT NULL dinâmico no Postgres?

Eu tenho uma coluna no meu banco de dados que é obrigatória APENAS se um outro field tiver um valor específico. Hoje essa validação é feita a nível de aplicação mas eu adicionei um check constraint para garantir a integridade de dados a nível de banco também.

Minha check constraint é a seguinte: (minha coluna type é um enum)

Check constraints:
    "code_filled_when_type_is_sent" CHECK (type <> 'sent'::type OR code IS NOT NULL) NOT VALID

essa check constraint faz a inserção falhar quando insiro um registro com type SENT e que o code não está inserido:

ERROR:  new row for relation "companies" violates check constraint "code_filled_when_type_is_sent" (PG::CheckViolation)

o que vocês acham dessa abordagem para colocar mais uma garantia na integridade de dados? :thinking:

Fala Cadu,
eu acho válido o ponto de realmente deixar o banco consistente.

Mas tenho um certo receio quanto a isso.

No caso você acabou inferindo uma regra de negócio no seu banco de dados.
Dependendo de como isso é documentado na sua empresa/contexto, pode gerar mais problemas do que benefícios.

Ex: Amanhã você sai da empresa, essa regra do ‘type’ <> ‘sent’ muda, talvez o próximo desenvolvedor que der manutenção nessa feature tenha dificuldade de achar o ponto de alteração no banco. Claro que esse exemplo é pequeno.

Se você está gerando essa constraint pelo codebase os problemas de rastreio ficam minimizados, então vai da cultura da empresa e de como estão documentando as coisas ai.

Se tornar isso uma prática comum e fácil, acho que consegue unir o melhor dos dois mundos.
Agora se for algo que você fez pontualmente então eu preferiria nem fazer e deixar só na APP mesmo.

[]s

2 curtidas

Concordo com o Damianijr, acaba sendo um trade-off de mais consistência em troca de acoplar uma regra de negócio na layer de persistência.

1 curtida

Boa @damianijr / @AugustoRavazoli !

Valeu pelos feedbacks!

Ví também uma dica do David Copeland no livro dele, o Sustainable Web Development With Ruby on Rails. Ele fala:

Here are the guidelines I find most useful:
Any stable requirement should be implemented as a check constraint.
Any critical requirement should be implemented as a check constraint.
Unstable requirements on tables expected to grow might be better implemented in code, so you can change them frequently, but it still might be better to use a check constraint and wait for the table to actually get large enough to be a problem.

E acho que faz sentido sim. O quão crítico é essa regra pra mim? Se for crítica, aí sim faz sentido talvez colocarmos a regra em mais uma camada.

Acho que o ponto é a criticidade, se é imprescindível que jamais essa regra seja comprometida, talvez valha o “double check” no banco.

Como vc já tem essa regra no código e possivelmente um teste pra ela, para cair na validação do banco precisaria que alguém removesse a validação e o teste. Caso aconteça isso, o banco é uma tentativa a mais de barrar isso, mas não impede a pessoa de remover do banco também da mesma forma que fez na aplicação, então é só um obstáculo a mais, pra talvez fazer a pessoa pensar “é, TAAALVEEEZ eu não devesse estar fazendo isso”.

Dai tenho duas dúvidas:

  • Esse ajuste no banco, fica versionado junto com o código da aplicação? Assim ele é mais visível do que sei lá, só algo que um DBA vai lá e roda o script direto na base e fica longe do código.

  • Vc consegue testar essa validação a nível de banco de forma automatizada? Pergunto pq um teste específico assim ajuda na documentação e deixa mais claro que tem essa regra a nível de banco implementada.

1 curtida

Fala @Lucius!! bom feedback. Vlww

Respondendo sua pergunta:

  • Sim. A check constraint ficaria versionada em migrations e no schema da app!
  • Consigo sim. Consigo fazer um teste unitário para validar a check constraint.
1 curtida