¿Qué son los casos de uso en la arquitectura limpia?
La arquitectura limpia, propuesta por Robert Martin, ofrece una estructura clara para organizar aplicaciones de software manteniendo su sostenibilidad y fácil mantenimiento. Dentro de este paradigma, los casos de uso juegan un papel clave, sirviendo como conductores para la aplicación de reglas de negocio específicas y permitiendo un flujo de información ordenado entre las capas de la aplicación. Pero, ¿qué son exactamente estos casos de uso y cómo se implementan?
¿Cómo se diferencian las reglas de negocio en los casos de uso?
En la arquitectura limpia, las reglas de negocio se dividen en dos categorías principales:
Reglas de negocio generales: Son aplicables a toda la organización y no están restringidas a un solo proyecto. Se implementan en el modelo de dominio o entidades de la aplicación.
Reglas de negocio específicas de la aplicación: Aplican exclusivamente a ciertas funcionalidades de un sistema particular, gestionadas mediante los casos de uso.
Esta distinción puede no ser predominante en todas las aplicaciones, especialmente si solo existe un sistema dentro de la organización.
¿Cómo se organiza y estructura un caso de uso?
Los casos de uso son herramientas vitales para coordinar la comunicación entre la capa externa y el modelo de dominio. Esto se logra mediante clases específicas para cada operación o funcionalidad dentro de la aplicación.
Organización en clases: Cada caso de uso suele implementarse en su propia clase. Esta organización clara minimiza las posibles interferencias entre diferentes funciones, facilitando su modificación y mantenimiento.
Este ejemplo en C# demuestra cómo un caso de uso puede recibir dependencias a través de inyección, realizar verificaciones necesarias y manipular datos específicos antes de devolver un resultado.
¿Es diferente de una capa de servicio?
Aunque los casos de uso y la capa de servicios puedan parecer similares, realmente se centran en aspectos diferentes. Los casos de uso ofrecen una estructura más especializada y detallada para dirigir operaciones precisas. Dependiendo del contexto, uno u otro puede ser más apropiado. En esencia, ambos métodos buscan garantizar que el dominio de la aplicación sea accesible de manera eficaz y estructurada.
¿Qué otros nombres pueden tener los casos de uso?
La nomenclatura para los casos de uso puede variar, lo que puede resultar confuso al revisar códigos ajenos. Algunas nomenclaturas comunes son:
Interactors: Muy utilizado en los ejemplos mencionados en bibliografía técnica.
Services: Más convencional, a menudo usado en otras arquitecturas.
Command Handlers: Empleado frecuentemente cuando se sigue un patrón CQRS (Command Query Responsibility Segregation).
Importancia de explorar diferentes ejemplos y lenguajes
La comprensión completa de los casos de uso y de la arquitectura limpia en su totalidad se alcanza mejor con la práctica y comparación. Explorando cómo estas ideas se implementan en diferentes lenguajes de programación, como C# o Java, se puede desarrollar una visión más clara. Además, analizar los ejemplos disponibles en repositorios open source puede proporcionar nuevas perspectivas sobre la estructuración eficiente de proyectos.
En resumen, los casos de uso son componentes críticos en la arquitectura limpia que facilitan una gestión clara y coherente de las funcionalidades de una aplicación, respaldando así el mantenimiento y evolución sostenible del software.
Contiene reglas de negocio específicas de la aplicación. Coordinan el flujo de datos desde y hacia el modelo de dominio.
Se suele implementar una clase por caso de uso.
Nota: Los casos de uso se suelen encontrar con los sufijos Interactor, Service o CommandHandler.
"clean-architecture-with-use-case".
Este archivo contiene una implementación de la clase RequestCourseRegistrationInteractor, que es un caso de uso relacionado con la registración de cursos. Aquí está el análisis de los ejemplos de casos de uso en el archivo:
1- La clase RequestCourseRegistrationInteractor implementa la interfaz IRequestHandler<CourseRegistrationRequestMessage, CourseRegistrationResponseMessage>. Esto indica que es responsable de manejar las solicitudes de registración de cursos y proporcionar una respuesta correspondiente.
2- Los parámetros del constructor de RequestCourseRegistrationInteractor son interfaces que representan los repositorios y servicios necesarios para realizar la registración de cursos. Estos incluyen IAuthService, IStudentRepository y ICourseRepository.
3- El método Handle es el punto de entrada del caso de uso. Toma un objeto CourseRegistrationRequestMessage como entrada y devuelve un objeto CourseRegistrationResponseMessage como resultado.
4- Dentro del método Handle, se realiza una verificación para determinar si el estudiante está autenticado antes de continuar con la registración. Si el estudiante no está autenticado, se devuelve una respuesta de error indicando que la operación ha fallado debido a la falta de autenticación.
5- Luego, se obtiene la instancia del estudiante utilizando el IStudentRepository y se crea una lista de errores para almacenar cualquier problema durante el proceso de registración.
6- A continuación, se itera sobre los códigos de los cursos seleccionados en el mensaje de solicitud y se obtiene la instancia de cada curso utilizando el ICourseRepository. Se intenta registrar al estudiante en cada curso mediante el método RegisterForCourse. Si la registración no tiene éxito, se agrega un mensaje de error a la lista de errores.
7- Después de completar la iteración, se guarda la instancia del estudiante actualizada utilizando el IStudentRepository.
8- Por último, se crea una instancia de CourseRegistrationResponseMessage con un indicador de éxito basado en la presencia o ausencia de errores, y se devuelve junto con la lista de errores.
En este repositorio en particular, se presenta una implementación de la Arquitectura Limpia utilizando casos de uso como un enfoque central. Los casos de uso encapsulan y implementan las reglas de negocio específicas de la aplicación, dirigiendo el flujo de datos entre las entidades y utilizando la lógica de negocio para lograr los objetivos establecidos. Los cambios en esta capa no deberían afectar a las entidades, y se espera que sean independientes de factores externos como la base de datos, el framework o la interfaz gráfica.
Además de los casos de uso, el repositorio incluye adaptadores de interfaz que se encargan de convertir los datos entre el formato más conveniente para los casos de uso y las entidades, y el formato aceptado por elementos externos como la base de datos o la interfaz de usuario. Estos adaptadores incluyen presentadores, vistas y controladores. También se hace hincapié en la separación de capas y en mantener los detalles específicos, como los frameworks y las herramientas, en el círculo externo de la arquitectura [1].
La relevancia de la arquitectura y diseño de sistemas en el desarrollo de software radica en la capacidad de crear soluciones robustas, mantenibles y escalables. Una arquitectura adecuada permite gestionar la complejidad del software, separando las preocupaciones y promoviendo la reutilización de componentes. Además, un diseño bien estructurado facilita la evolución y adaptación del sistema a medida que los requisitos cambian. La Arquitectura Limpia y el enfoque de casos de uso promueven estos principios, proporcionando una estructura clara y una separación de responsabilidades que ayuda a desarrollar sistemas de alta calidad y fáciles de mantener.
Curiosamente nunca he visto un proyecto con Interactor, pero si he visto y he trabajado con proyectos que usan CommandHandler y Service 🤔
Lo unico que no me gusta de usar request y response para hablar de entrada y salida de los casos de uso, es que es muy similar a como funcionan los middleware de apps web, como insinuando que los casos de uso son estrictamente llamados desde la web. Pero en realidad un caso de uso podria llamarse desde una job queue, crontab, task ejecutada por medio de un script, etc. En esos casos, no es muy natural llamarles request y response, aunque es un detalle de nombramiento nada mas.
En un proyecto donde implementamos Hexagonal usamos el prefijo “UseCase” y para los servicios de domino “Service”. Esto puede variar según los proyectos y equipos, lo más importante es manejarlos con claridad a nivel empresarial.
Estos casos de uso se conectan con los casos de uso que se documentan con diagramas UML o son diferentes?
Mauricio, son exactamente lo mismo.
Los casos de uso que documentas en un diagrama UML (como "Registrar Usuario" o "Realizar Pago") son la definición funcional de lo que el sistema debe hacer. En Clean Architecture, las clases que implementan el método Handle son simplemente la materialización técnica de esos mismos diagramas.
Piensa en el diagrama UML como el "plano" de una casa y en la clase Handle como la "habitación" construida siguiendo ese plano. La intención es que exista una correspondencia directa: si en tu diagrama tienes un caso de uso, en tu código deberías tener una clase dedicada a ejecutar esa lógica específica.
Esta estructura garantiza que, si el negocio cambia, sepas exactamente qué clase modificar sin afectar el resto del sistema. Para ver cómo esto se traduce en una estructura de capas, te recomiendo Desglose de Capas en Clean Architecture.
El método handle de C# es equivalente al método invoke de PHP?
Hola, Mauricio. Así es, son funcionalmente equivalentes.
En el contexto de Clean Architecture, el método Handle en C# y el método __invoke en PHP cumplen exactamente el mismo propósito: ejecutar la lógica principal de un caso de uso.
Ambos siguen el patrón Command o Single Action Controller, donde una clase encapsula una única operación de negocio. La diferencia es puramente sintáctica:
C# (Handle): Es el nombre estándar definido por la interfaz IRequestHandler para procesar la solicitud.
PHP (__invoke): Es un método mágico que permite que el objeto sea llamado como si fuera una función, facilitando la ejecución directa del caso de uso.
Los CU son reglas de negocio de la aplicación, es el orquestador del dominio y donde se arma la lógica, con validaciones y/o cálculos abstraídos en los dominios correspondientes.
Ejemplos de CU:
createSubscription
registrationCourse
cancelSubscription
Los casos de uso yo los entiendo por casos especifico, por Ejemplo, si tu App tiene una seccion que se llama 'Cuenta', en esta se van a crear casos de uso: como actualización de foto, datos, cambio de contraseña. y demas, ya que son acciones especificas de la seccion o de la app.