DDD en Rust: cómo escribir código de dominio sin dolor ni macros

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Domain-Driven Design en proyectos Rust: cuando los tipos se convierten en sus aliados



¿Alguna vez ha escrito una aplicación Rust en la que la lógica de negocio se ahogaba en un mar de estructuras, traits y macros? ¿Esa sensación familiar de que, al mirar el código, no puede decir de inmediato qué hace tal o cual función en el contexto del dominio? Si es así, bienvenido al club. Domain-Driven Design (DDD) está diseñado para resolver este problema, y Rust con su sistema de tipos es el terreno ideal para implementar los principios de DDD.



En este artículo no vamos a resumir el libro de Evans. En su lugar, mostraré cómo aplicar Domain-Driven Design en proyectos Rust con ejemplos reales. Aprenderá cómo, mediante tipos envoltorio, arquitectura modular y estructuras expresivas, hacer que el código de dominio sea limpio, seguro y comprensible. ¡Allá vamos!



Por qué Rust y DDD son la pareja ideal



Domain-Driven Design exige un aislamiento estricto de la lógica de dominio respecto a la infraestructura. Rust proporciona herramientas potentes para ello:



  • Sistema de tipos — permite modelar reglas de negocio a nivel del compilador. ¿Un error? El compilador no lo permitirá.
  • Propiedad y préstamo — garantizan que los agregados y entidades no sean modificados accidentalmente en un lugar inesperado.
  • Sistema de módulos — separa claramente la capa de dominio, la infraestructura y la aplicación.


Pero también hay trampas. Rust no perdona los objetos «gordos» con un montón de estado mutable. Por eso, DDD en Rust requiere un enfoque de modelado diferente al de, por ejemplo, Java o C#.



Idea clave: haga que los estados inválidos sean irrepresentables



En DDD buscamos que las reglas de negocio estén codificadas en la propia estructura de datos. En Rust esto se convierte en un mantra: «Si un estado no es válido, haga que sea imposible crearlo».



En lugar de escribir:



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


Creamos tipos que ya en tiempo de compilación garantizan la corrección:



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

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


¿Parecen simples envoltorios? En realidad, este es el primer paso hacia DDD en Rust, donde los tipos se convierten en documentación y guardianes de los invariantes.



Modelamos el dominio: Value Object y Entity



Los conceptos centrales de DDD son Entity (entidad) y Value Object (objeto-valor). En Rust se implementan de manera diferente.



Value Object en Rust: inmutabilidad e igualdad por valor



Un Value Object no tiene identidad. Dos objetos son iguales si sus campos son iguales. En Rust encaja perfectamente con estructuras que usan #[derive(PartialEq)] y campos inmutables.



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 { // Regla de negocio: el dinero no puede 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 es inmutable. Cualquier operación devuelve un nuevo objeto. Esto se alinea con la filosofía de DDD en Rust: nada de setters, solo métodos que crean nuevas instancias.



Entity: identidad

Blogs

Book Recommendations