DDD em Rust: como escrever código de domínio sem dor e sem macros

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Domain-Driven Design em projetos Rust: quando os tipos se tornam seus aliados



Você já escreveu uma aplicação Rust onde a lógica de negócio se afogava em um mar de structs, traits e macros? Sente aquela familiaridade quando, olhando para o código, você não consegue dizer de imediato o que determinada função faz no contexto do domínio? Se sim — bem-vindo ao clube. O Domain-Driven Design (DDD) visa resolver esse problema, e o Rust com seu sistema de tipos é o terreno ideal para implementar os princípios do DDD.



Neste artigo, não vamos recontar o livro de Evans. Em vez disso, mostrarei como aplicar Domain-Driven Design em projetos Rust com exemplos reais. Você aprenderá como, usando tipos-embrulho, arquitetura modular e structs expressivas, tornar o código de domínio limpo, seguro e compreensível. Vamos nessa!



Por que Rust e DDD são a dupla perfeita



Domain-Driven Design exige um isolamento rigoroso da lógica de domínio da infraestrutura. O Rust fornece ferramentas poderosas para isso:



  • Sistema de tipos — permite modelar regras de negócio no nível do compilador. Um erro? O compilador não deixará você cometê-lo.
  • Propriedade e empréstimo — garantem que agregados e entidades não sejam alterados acidentalmente em lugares inesperados.
  • Sistema de módulos — separa claramente a camada de domínio, infraestrutura e aplicação.


Mas também existem armadilhas. O Rust não perdoa objetos "gordos" com um monte de estado mutável. Portanto, o DDD em Rust exige uma abordagem diferente para modelagem do que, digamos, em Java ou C#.



Ideia-chave: torne os estados inválidos irrepresentáveis



No DDD, buscamos que as regras de negócio estejam codificadas na própria estrutura de dados. No Rust, isso se transforma em um mantra: "Se um estado é inválido — torne impossível criá-lo".



Em vez de escrever:



struct User {    name: String,    email: String,    age: u8,}


Criamos tipos que já em tempo de compilação garantem a correção:



pub struct Email(String);pub struct Age(u8);pub struct UserName(String);

pub struct User { name: UserName, email: Email, age: Age,}


Parece que são apenas wrappers? Na verdade, este é o primeiro passo para o DDD em Rust, onde os tipos se tornam documentação e guardiões dos invariantes.



Modelando o domínio: Value Object e Entity



Conceitos centrais do DDD — Entity (entidade) e Value Object (objeto de valor). No Rust, eles são implementados de forma diferente.



Value Object em Rust: imutabilidade e igualdade por valor



Value Object não possui identidade. Dois objetos são iguais se seus campos são iguais. Em Rust, encaixa-se perfeitamente com structs usando #[derive(PartialEq)] e campos imutáveis.



use std::fmt;

#[derive(Debug, Clone, PartialEq)]pub struct Money { amount: i64, currency: Currency,}

#[derive(Debug, Clone, PartialEq)]pub enum Currency { USD, EUR, RUB,}

impl Money { pub fn new(amount: i64, currency: Currency) -> Self { // Regra de negócio: dinheiro não pode ser negativo assert!(amount >= 0, "Amount must be non-negative"); Self { amount, currency } }

pub fn add(&self, other: &Money) -> Result<Self, String> { if self.currency != other.currency { return Err("Cannot add different currencies".into()); } Ok(Self::new(self.amount + other.amount, self.currency.clone())) }}


Observe: Money é imutável. Qualquer operação retorna um novo objeto. Isso está alinhado com a filosofia do DDD em Rust: sem setters, apenas métodos que criam novas instâncias.



Entity: identidade

Blogs

Book Recommendations