Effects
Todo programa útil termina saliendo de sí mismo. Lee una base de datos, consulta un servicio, mira el reloj o escribe un mensaje. El problema no es que existan esas operaciones; el problema aparece cuando no podemos distinguirlas de la regla que decidió ejecutarlas.
fx/ agrupa lo que toca el exterior.
¿Por qué fx/ y no infrastructure/? Infraestructura es un nombre válido, pero puede abarcar muchas cosas. En DFE uso fx/ para encontrar rápidamente las funciones que leen o escriben datos, llaman a otros servicios o producen efectos fuera del programa. La carpeta dice qué hacen esas funciones; no obliga a esconderlas detrás de repositorios o adaptadores. Si el proyecto necesita esas abstracciones, pueden añadirse con un motivo concreto.
fx/
├── db/
├── cache/
└── events/
Una función de fx/db hace un SELECT o un UPDATE. No se esconde detrás de un puerto para parecer desacoplada.
pub async fn get_product(
tx: &mut PgConnection,
product_id: ProductId,
) -> Result<Option<Product>, sqlx::Error> {
// SELECT
}
pub async fn update_cart(
tx: &mut Transaction<'_, Postgres>,
cart: &Cart,
) -> Result<(), sqlx::Error> {
// UPDATE
}Consultar datos y producir cambios
En DFE uso fx/ para agrupar las operaciones que interactúan con el exterior. Aquí hay consultas y escrituras. El nombre no significa que esté aplicando una clasificación de efectos de la programación funcional.
Para organizar una operación, distingo entre obtener información y modificar el sistema. Consultar el stock no cambia por sí mismo el estado del negocio; guardar el carrito sí. Ambas funciones viven en fx/db, pero no las llama el mismo lugar: el flow consulta y API guarda.
Cuando aquí hablo de efectos sobre el sistema, me refiero a cambios o acciones: persistir el carrito, invalidar una caché, enviar un correo o publicar un evento. Una consulta que solo obtiene información no produce ese cambio. Leer el reloj tampoco; aun así, depende del entorno y tiene su lugar en fx/.
Eso no convierte las lecturas en funciones puras. Una consulta puede devolver otro resultado con los mismos argumentos, fallar o adquirir bloqueos. Llamarla lectura tampoco basta si ejecuta una función que modifica datos. Esta distinción habla del estado del negocio, no de la ausencia de toda actividad en la infraestructura.
Qué coordina cada lugar
El flow usa las consultas de fx/db, aplica las reglas y devuelve el resultado. La API ejecuta la escritura mediante fx/db y confirma la transacción. Si la regla necesita que la consulta y la escritura formen una unidad, API conserva la misma transacción durante ambos pasos.
Invalidar una caché, enviar un correo o publicar un evento también queda en api/, mediante el resto de fx/, después del commit. Ni las escrituras ni esas acciones se ejecutan dentro del flow. No estamos exigiendo pureza funcional: permitimos consultar información externa, pero dejamos los cambios sobre el sistema a la vista en API.