Manifiesto

Domain–Flow–Effects (DFE) es una propuesta para organizar servicios backend en Rust alrededor de una idea: la estructura debe ayudar a entender un cambio, no convertirse en otro problema que resolver.

Cuando las reglas, las operaciones y las interacciones con el exterior no tienen lugares reconocibles, mantener un sistema exige reconstruirlo antes de poder modificarlo. DFE busca reducir ese costo: que sea posible encontrar qué decide el negocio, qué operación se ejecuta y dónde el sistema produce efectos.

Cómo llegué a esta propuesta

Cuando empecé a trabajar con Rust, intenté organizar mis proyectos con estructuras que ya conocía de otros lenguajes. Algunas ideas me servían, pero ciertas implementaciones no se sentían naturales en Rust. La inyección de dependencias fue uno de los casos más claros. Para pasar una dependencia de un componente a otro tenía que ocuparme de ownership, borrowing y lifetimes; enseguida aparecían traits, implementaciones y más capas. Se podía hacer, pero a veces la solución terminaba exigiendo más atención que el problema que quería resolver.

Crear un trait no terminaba ese trabajo. Todavía tenía que decidir quién poseía la dependencia, cómo la prestaba y cómo mantenía esos préstamos durante una operación asíncrona. Rust ofrece varias formas de resolverlo, con genéricos o despacho dinámico, y no todas necesitan Arc. Pero si añadía esa separación solo para cumplir con la arquitectura que había elegido, acababa dedicando código a sostener una abstracción que mi operación todavía no necesitaba. Esa dificultad fue una de las razones para buscar otra forma de organizar el proyecto. La página de flows desarrolla esta decisión y sus costos.

No pensaba que esas herramientas estuvieran mal. Empecé a preguntarme algo más sencillo: ¿necesitaba todas esas piezas en ese proyecto? En vez de reproducir cada patrón tal como lo conocía, traté de entender qué problema resolvía y qué podía aprovechar de Rust sin añadir complejidad antes de tiempo.

Así fui llegando a DFE: una convención que refleja cómo prefiero organizar sistemas Rust. Las reglas del negocio tienen su lugar, las operaciones que las aplican se pueden seguir y los efectos que conectan el programa con el exterior quedan a la vista. No es una respuesta definitiva para todas las arquitecturas; es la forma que me resultó más clara para trabajar en estos proyectos.

Compromisos

DFE distingue responsabilidades que conviene poder reconocer al leer el código:

Lugar Responsabilidad
domain/ Expresar conceptos, reglas e invariantes del negocio
flows/ Consultar datos, aplicar las reglas y devolver el resultado sin guardarlo
fx/ Hacer explícitas las interacciones con la base de datos y otros sistemas
api/ Recibir peticiones, llamar al flow, guardar el resultado y coordinar sus consecuencias

Esta separación es una guía de lectura, no una obligación de atravesar cuatro pasos en cada llamada. Una operación técnica sencilla puede ir directamente de api/ a una función de fx/; una operación con reglas de negocio se expresa como un flow. La estructura debe corresponder al trabajo real del sistema.

¿Por qué elegir DFE?

Clean Architecture y la arquitectura hexagonal han ayudado a muchos equipos a separar responsabilidades y controlar las dependencias. No considero que estén mal, ni que DFE resuelva todos los problemas mejor que ellas. Sus principios también se pueden aplicar en Rust.

Lo que propongo con DFE es una convención concreta para llevar esas ideas al código Rust: el dominio expresa las reglas, un flow muestra la operación, fx/ reúne las interacciones con el exterior y api/ hace visible la entrada y la coordinación. Así, quien llega al proyecto tiene un lugar claro por dónde empezar a leer.

En lo personal, prefiero empezar con las funciones y los datos que Rust ya hace explícitos. Un flow puede llamar a fx/db directamente, y el compilador señala qué lugares debo revisar si cambia esa función. No necesito añadir desde el principio un trait, un repositorio y una capa de inyección si todavía no resuelven una necesidad concreta. A cambio, el flow queda ligado de forma visible a esas funciones. No intento esconder ese acoplamiento detrás de una interfaz que quizá nunca necesite.

¿Cuándo usaría otra estructura, o combinaría DFE con esas ideas? Si el proyecto necesita intercambiar implementaciones o marcar una frontera importante, un trait, un puerto o un adaptador pueden ser justo lo que hace falta. DFE no los prohíbe; prefiero incorporarlos cuando resuelven una necesidad real.

Elegir DFE no significa elegir una arquitectura superior a las demás. Significa que, para algunos sistemas Rust, prefiero empezar con una estructura sencilla y explícita, y añadir capas cuando el proyecto las necesite. Si otra arquitectura te ayuda más a entender y cambiar tu sistema, úsala.

Lo que defendemos

Estos compromisos buscan que un cambio pueda razonarse con contexto acotado: qué regla cambia, qué operación la aplica y qué efectos pueden resultar. No prometen que un sistema complejo sea pequeño ni que todo cambio afecte a un solo archivo. Prometen una estructura que ayude a seguir el significado del cambio.

Límites

DFE no pretende resolver todos los problemas de arquitectura ni definir dónde debe ir cada archivo del proyecto. No prescribe una estrategia de despliegue, observabilidad, tenancy o mensajería. Tampoco exige caché, eventos, una capa de servicios ni una abstracción para cada dependencia. Esas decisiones dependen del sistema y deben justificarse por sus necesidades.

La arquitectura es útil en la medida en que mejora la lectura y el cambio. Si sus nombres o divisiones dejan de aportar significado, deben revisarse. Añadir una carpeta no es progreso por sí mismo; quitar una tampoco lo es.

Para qué proyectos

DFE nace de desarrollar backend: servicios que reciben peticiones o trabajos, consultan datos, aplican reglas de negocio y guardan cambios. No exige HTTP, gRPC ni microservicios; ese es el tipo de trabajo para el que propongo esta organización.

No es una arquitectura universal para Rust. Kernels, drivers, sistemas embebidos, bibliotecas, compiladores y motores tienen otras necesidades de diseño. Para una CLI o un script pequeño puede bastar una estructura plana. Algunos principios pueden servir fuera del backend, pero no propongo trasladar allí toda la estructura de DFE.

Si otra arquitectura ya funciona en tu proyecto, no hace falta cambiarla por DFE. Sigo refinando esta propuesta; en Autor cuento desde qué experiencia la comparto.

Una pregunta para evaluar el diseño

Antes de añadir una abstracción o una nueva capa, me pregunto:

¿Qué significado, límite o necesidad concreta hace visible?

Si no puedo responder con claridad, quizá el código esté mejor sin ella.

Origen

El artículo Domain–Flow–Effects (DFE): una arquitectura pensada para Rust (en español) amplía este recorrido con ejemplos. La página de arquitectura y las demás secciones desarrollan cómo aplicar la propuesta.