DDD funcional con dry-rb es el mejor sabor de domain modeling en Ruby
Un coworker que viene de Rails me preguntó por dónde entrar a DDD. Le iba a recomendar el approach clásico — modelos enriquecidos, aggregates, el libro azul de Evans — pero me detuve. “Modelos enriquecidos” en Rails suena demasiado parecido a lo que ya tiene: modelos de ActiveRecord con lógica. La intención es completamente distinta pero el nombre confunde.
Le recomendé el DDD funcional con dry-rb directamente. La diferencia se entiende mejor con código:
# No es un ActiveRecord — no toca la DB, es inmutable
class Money < Dry::Struct
attribute :amount, Types::Decimal
attribute :currency, Types::String.constrained(included_in: %w[MXN USD EUR])
end
# El flujo falla explícitamente con tipos, no con excepciones ni nil
class ProcessPayment
include Dry::Monads[:result]
def call(order, payment_method)
return Failure(:invalid_order) unless order.payable?
charge = payment_gateway.charge(order.total, payment_method)
charge.declined? ? Failure(:charge_declined) : Success(charge)
end
end
La lógica de negocio no depende de la DB, se testea sin Rails, y falla de forma explícita. Lo que se pierde es la velocidad de arranque de Rails convencional — vale la pena solo en dominio complejo, no en CRUD.
El libro que lo explica mejor: Domain Modeling Made Functional de Scott Wlaschin. Está en F# pero dry-rb es básicamente la misma filosofía portada a Ruby.