Modelar o domínio
Linguagem ubíqua, event storming e onde traçar a fronteira de um agregado.
O código de domínio é tão bom quanto as palavras que ele usa. Se o negócio diz "o pedido foi faturado" e o código diz status = 3, cada conversa entre as duas pontas precisa de tradução — e a tradução é onde os requisitos se perdem.
Event storming, em três passos#
- Os fatos, no passado. Numa parede (ou num documento), todo mundo escreve o que acontece no negócio:
PedidoFeito,PagamentoConfirmado,PedidoEnviado. Discutir o nome de um fato é discutir o negócio. - O que os causa. Antes de cada fato, o comando que o provoca (
Pagar) e quem o dá (o cliente, um sistema, o relógio). - O que precisa ser consistente junto. Os fatos que não podem divergir entre si formam um agregado. O resto se comunica por evento.
A fronteira do agregado#
| Sinal | O que ele diz |
|---|---|
| dois comandos do mesmo agregado raramente disputam | o tamanho está bom |
| `AggregateVersionError` frequente | o agregado junta coisas que mudam por motivos diferentes — divida |
| uma invariante precisa de dois agregados | ou eles são um só, ou a regra é eventual e vira processo |
| o agregado carrega mil itens para mudar um | a coleção deveria ser outro agregado, ligado por id |
dataforge
adopt Arcane.Dominio as D
// a linguagem do negócio, e não a do banco
steady Dinheiro := D.valor("Dinheiro", ["centavos", "moeda"],
regra := lambda v => v["centavos"] bigger_eq 0, motivo := "dinheiro não é negativo")
pedido := D.agregado("Pedido", D.novo_id(), situacao := "aberto", total := Dinheiro(0, "BRL"))
mark @pedido.comando("faturar")
action faturar(p):
given p.ler("situacao") is not "aberto":
trigger "só um pedido aberto é faturado"
p.mudar(situacao := "faturado")
p.aconteceu("PedidoFaturado")
pedido.faturar()
assert pedido.ler("situacao") is "faturado"
assert pedido.eventos()[0].nome is "PedidoFaturado"