Ir al contenido
  1. Posts/

Todo CLI en Rust 1. Arquitectura hexagonal en un proyecto pequeno

·2 mins
Rafael Fernandez
Autor
Rafael Fernandez
Matemáticas, programación y cosas de la vida
Todo CLI en Rust sin humo - Este artículo es parte de una serie.
Parte 1: Este artículo

Muy buenas.

Read this chapter in English: EN

Esta serie cuenta cómo construimos todo-cli-rs partiendo del Project #1 de CodeCrafters (Rust Projects). Si quieres ir mirando el código mientras lees, el repo está aquí:

El reto parecía simple (hasta que dejó de serlo)
#

El alcance inicial era muy clásico:

  • add
  • list
  • done
  • delete
  • persistencia local

Con eso, podíamos montar un main.rs enorme, tocar un JSON y listo.

Pero había una decisión más importante que “qué comando va primero”: queríamos que este proyecto sirviera como laboratorio serio de Rust, no como script desechable.

La decisión que cambió todo
#

Antes de escribir casos de uso, definimos estructura:

  • Hexagonal Architecture para separar dominio de infraestructura.
  • Screaming Architecture para que la carpeta grite “tasks” y no “utils”.

Puedes verlo en:

Por qué esto en vez de un diseño más plano
#

Opción rápida:

  • CLI parsea,
  • hace validaciones,
  • escribe JSON,
  • imprime salida.

Funciona mientras el alcance es mínimo. Se rompe cuando quieres evolucionar.

Ejemplo real: añadir una segunda persistencia (por ejemplo SQLite).

  • En diseño plano, tocas parsing, lógica de negocio, tests y salida, porque todo está acoplado.
  • En diseño hexagonal, cambias adapter y conservas dominio + use cases.

Ese “coste extra” inicial te ahorra semanas de refactor torpe cuando la app crece.

El primer contrato que escribimos
#

Definimos los comandos de forma explícita:

  • add <title>
  • list [--status <all|todo|done>]
  • done <id>
  • todo <id>
  • delete <id>
  • --output table|json

Esto vive en:

No fue solo una decisión de UX. Fue diseño de sistema: delimitar bien entradas evita ambigüedad en toda la aplicación.

Mapa de capas (con responsabilidad concreta)
#

  • domain: reglas de negocio puras.
  • application/use_cases: orquestación de flujos.
  • ports: contratos para que aplicación no conozca infraestructura.
  • adapters/cli: entrada/salida terminal.
  • adapters/persistence: repositorios concretos.

Código base de módulos:

Punto de inflexión real
#

Cuando más se notó esta elección fue al introducir output dual (table/json) y persistencia en fichero. Ningún caso de uso tuvo que aprender de serde_json, rutas del sistema o println! con formato. Todo quedó en adapters.

Esa es la señal de que la arquitectura está haciendo su trabajo.

Cierre
#

En esta primera parte lo importante no era “tener comandos funcionando”, sino fijar reglas de juego para que el proyecto no se convierta en deuda con patas.

En la siguiente entrega entramos al núcleo: dominio inmutable, transiciones y taxonomía de errores por capa.

Todo CLI en Rust sin humo - Este artículo es parte de una serie.
Parte 1: Este artículo