
Duas requisições reservam a última unidade. Cada uma leu disponibilidade igual a um e aprovou sua própria compra. As classes seguem o vocabulário do negócio, mas o software vendeu uma garantia que não conseguiu preservar. Chamar uma classe de agregado não coordena gravações concorrentes.
Implementing Domain-Driven Design, de Vaughn Vernon, é uma recomendação para quem quer aproximar modelagem e implementação. Sua leitura vale quando você consegue nomear conceitos do domínio, mas ainda precisa decidir quais regras exigem uma transação, o que pode acontecer depois e como essas escolhas chegam ao código.
A ficha da Addison-Wesley/Pearson registra primeira edição publicada em 6 de fevereiro de 2013, com copyright de 2013. A obra conecta contextos e mapeamento a entidades, valores, eventos, agregados e repositórios. Os exemplos em Java acompanham um SaaS ligado à gestão ágil de projetos.
Minha sugestão é preservar essa ligação entre estratégia e detalhes. Um agregado bem implementado dentro do contexto errado continua protegendo uma interpretação inadequada do negócio. Antes de discutir concorrência, confirme o significado de reservar: segurar estoque por um período é diferente de registrar uma intenção de compra sem garantia.
No capítulo Model True Invariants in Consistency Boundaries, Vernon apresenta o agregado como fronteira de consistência transacional, guiada por invariantes reais. Essa é a diferença central em relação a agrupar objetos porque o ORM permite navegar entre eles.
Para esta resenha, considere um estoque hipotético de um único item por depósito. A política aceita uma reserva apenas se houver quantidade livre. As quantidades são inteiras, a solicitação precisa ser positiva e a invariante é 0 <= reservado <= emEstoque. A disponibilidade deriva de emEstoque - reservado.
Pedido e pagamento não precisam participar dessa conta para impedir duas reservas da última unidade. Já duas gravações no mesmo saldo precisam de coordenação. O modelo pode decidir sobre a regra em memória; a persistência precisa manter a validade dessa decisão diante de outras operações. Uma dessas partes isolada não oferece a garantia completa.
Este exercício original descreve um resultado esperado, não uma execução nem uma prova sobre um banco. Estado inicial: item SKU-7, depósito D-1, emEstoque=1, reservado=0, versao=7. Os comandos A e B solicitam uma unidade cada, para pedidos diferentes, lendo a mesma versão inicial.
Adote controle otimista neste desenho: salvar exige que a versão persistida ainda seja a versão lida. A operação vencedora incrementa a versão. Outras estratégias são possíveis, mas precisam preservar a mesma regra comercial. A ordem de vitória abaixo é escolhida apenas para facilitar a explicação.
| Passo | Resultado esperado | Estado persistido após o passo |
|---|---|---|
A e B leem versão 7 | Ambos veem uma unidade livre | Estoque 1, reservado 0, versão 7 |
A salva exigindo versão 7 | Reserva R-1 aceita | Estoque 1, reservado 1, versão 8 |
B tenta salvar exigindo versão 7 | Conflito; nenhuma gravação do saldo | Estoque 1, reservado 1, versão 8 |
B relê e reavalia a regra | Recusa comercial: sem disponibilidade | Estoque 1, reservado 1, versão 8 |
A repete o mesmo comando após timeout | Recebe o resultado de R-1 | Estoque 1, reservado 1, versão 8 |
O conflito técnico não é a própria recusa por falta de estoque. Outro tipo de alteração poderia ter avançado a versão sem consumir a unidade. Por isso o perdedor precisa reler e decidir com dados atuais. Repetir cegamente uma gravação aprovada sobre a versão antiga reapresenta justamente o risco que queríamos eliminar.
O comando A contém identidade estável, item, depósito, pedido e quantidade. Defina a chave de deduplicação com seu escopo, por exemplo (deposito, item, commandId). Se a mesma chave reaparecer com quantidade ou pedido diferente, devolva conflito de conteúdo. Reaproveitar identificadores indiscriminadamente não oferece idempotência.
Neste desenho, a primeira aceitação registra o resultado do comando e altera o saldo atomicamente. Uma restrição única impede dois registros concorrentes da mesma chave. A checagem de versão participa dessa mesma unidade de confirmação. Se um desses passos falhar, o conjunto não pode ficar parcialmente aplicado.
Checar a chave, sair da transação e só depois reservar cria outra corrida. Registrar apenas depois de responder também deixa uma janela para duplicação. O repositório precisa tornar explícitas as condições de salvamento e conflito; um método genérico save() que esconde essas decisões dificulta avaliar a garantia.
O registro técnico do comando pode viver numa tabela auxiliar sem transformar todos os pedidos em parte do agregado de estoque. Isso exige uma decisão concreta de armazenamento e transação. Também exige política de retenção: se a deduplicação expira, o contrato deve dizer até quando uma repetição é reconhecida, inclusive depois de uma reserva encerrada.
Produtos gratuitos e pagos para transformar ideias em uma base que você consegue executar.
13 produtos disponíveisContinue explorando tópicos similares

Leia Eric Evans a partir de um conflito de vocabulário e descubra onde um modelo precisa de limites explícitos.

Aplique Michael Nygard a timeouts, isolamento e recuperação com um exercício de falha controlada.

Transforme estudo passivo em projeto de aprendizagem com prática direta, feedback e recuperação espaçada.
Checklist de 47 pontos para encontrar bugs, riscos de segurança e problemas de performance antes do lançamento.
Templates testados em produção, usados por desenvolvedores. Economize semanas de setup no seu próximo projeto.
Representar quantidade e identidade do item com tipos específicos pode impedir combinações inválidas antes da alteração do saldo. A utilidade aparece quando o tipo recusa uma quantidade negativa ou distingue depósito de pedido. Criar wrappers para todo campo sem restringir operação alguma apenas acrescenta cerimônia.
Ao aceitar R-1, o sistema pode produzir o fato ReservaAceita, com reserva, item, depósito, quantidade e referência ao pedido. Esse fato tem significado no contexto de estoque. O contexto de pedidos decide como traduzi-lo para seu estado, sem compartilhar o objeto inteiro de saldo.
No ensaio de 2011, segunda parte de Effective Aggregate Design, Vernon discute referências entre agregados e consistência eventual, incluindo recuperação de falhas. Aplicando essa preocupação aqui, um evento criado em memória e perdido após a gravação não informa pedidos. Um mecanismo como outbox precisa registrar a intenção de publicação junto à aceitação e depois entregar com repetição controlada.
Outbox é uma opção deste exercício, não garantia automática de entrega única. O consumidor deve reconhecer mensagens repetidas e aplicar sua transição com proteção apropriada. Entre os contextos, não existe uma transação fictícia só porque ambos usam o mesmo nome de evento.
Depois da reserva, o pedido pode ficar temporariamente aguardando confirmação. A interface deve distinguir esse estado de uma compra concluída. Um timeout não informa, sozinho, se o estoque confirmou ou recusou. Ofereça consulta pelo identificador do comando ou da reserva para recuperar uma resposta perdida.
A política também precisa determinar quando R-1 expira e como libera a unidade. Expiração e confirmação de consumo podem disputar a mesma reserva; o estado terminal deve impedir liberar duas vezes ou consumir depois de expirar sem nova decisão. Resolver apenas a criação da reserva deixa metade do ciclo descoberta.
Defina responsável por pendências, tentativas limitadas e reconciliação. Se pagamento acontecer após expiração, alguém precisa decidir entre nova reserva, estorno ou atendimento. A consistência eventual aumenta espaço para processos intermediários; sua adoção precisa comprar uma vantagem suficiente para sustentar esse trabalho.
Colocar estoque, pedidos, clientes e pagamentos no mesmo agregado faria alterações sem relação disputar uma fronteira. A navegação conveniente do ORM pode esconder carregamento excessivo e concorrência desnecessária. Pergunte qual invariante exige cada elemento antes de incluí-lo.
O extremo oposto também falha: separar cada linha em um agregado pode espalhar a própria regra de disponibilidade por gravações independentes. Pequeno é uma direção de projeto, não um objetivo que supera o contrato. Uma última unidade muito disputada continua exigindo arbitragem, mesmo com uma fronteira bem escolhida.
Um carrinho com dois itens acrescenta outra pergunta: pode reservar o primeiro se o segundo estiver indisponível? Se a política exige tudo ou nada, o exemplo de um único saldo não basta. É preciso negociar reserva parcial, compensação ou outra fronteira transacional. Adicionar um identificador de carrinho aos comandos não cria essa garantia. Registre a decisão antes de prometer confirmação integral na interface. Uma reserva local válida ainda pode contrariar a expectativa sobre a compra completa.
Também evite carregar um histórico ilimitado de reservas no saldo. A implementação precisa planejar registros ativos, auditoria e consultas sem perder as garantias de liberação. Isso pede análise do acesso aos dados; o diagrama do agregado não resolve volume, índices ou contenção de um item popular.
Um teste unitário pode verificar quantidade inválida e disponibilidade insuficiente. Para a tabela acima, use também o armazenamento real: force duas transações a ler a versão 7, permita a primeira confirmação e verifique a rejeição da segunda. Confirme que o saldo final tem apenas uma unidade reservada.
Acrescente dois comandos iguais concorrentes, repetição após timeout e reutilização da chave com conteúdo diferente. Simule falha antes da confirmação e entre confirmação e publicação. Verifique saldo, resultado registrado e retomada do envio. As expectativas precisam sobreviver ao isolamento e às restrições efetivamente configuradas no banco.
Nenhum desses testes foi executado por esta resenha, que apresenta um contrato de exercício. Uma simulação sequencial em memória ajudaria a entender o modelo, mas não demonstraria a propriedade concorrente do armazenamento. Essa diferença deve permanecer visível na documentação da implementação.
Recomendo o livro para quem conhece a proposta de Evans e já trabalha com persistência e transações. Ele ajuda a transformar nomes de padrões em perguntas verificáveis. O estudo de um domínio contínuo oferece contexto para conectar decisões, sem reduzir tudo a pequenos exemplos independentes.
A edição exige adaptação ao ambiente atual. Seus exemplos Java não devem virar uma obrigação de reproduzir hierarquias, frameworks ou mapeamentos. O modelo de domínio precisa servir às regras; o ORM atende à persistência. Quando a conveniência de um define silenciosamente a fronteira do outro, a modelagem perdeu sua justificativa.
Para operações CRUD com regras simples, uma transação direta e validações claras podem bastar. Adie a adoção extensa se não há problema de linguagem ou consistência que a justifique. Uma equipe sem capacidade de operar reprocessamento também deve considerar cuidadosamente o custo de introduzir integração assíncrona.
Evans ajuda a investigar significado e contexto. Clean Architecture complementa a separação de dependências. DDIA aprofunda mecanismos de dados e concorrência. Nenhum desenho de camadas escolhe sozinho a invariante comercial; nenhum nome de agregado substitui a garantia do armazenamento.
Leia estratégia antes de transportar os padrões táticos para um projeto. Depois escolha uma invariante, escreva estados e comandos como os da tabela e examine a transação correspondente. Discuta falhas e recuperação com a equipe que operará o sistema. Esse é um plano de estudo, sem promessa de redesenhar toda a aplicação.
O motivo para recomendar Vernon é essa aproximação entre compromisso comercial e implementação. Você aproveitou a leitura quando consegue explicar o que permanece consistente na confirmação, o que pode atrasar e como o sistema retoma trabalho interrompido. A quantidade de classes com nomes de DDD é uma medida muito menos útil.