Separando la logica de la capa de negocios en casos de uso.

En este post quiero explicar como se puede lograr una separacion entre a logica de la aplicacion o capa de reglas de negocio del resto de las capas de la arquitectura que estemos utilizando. El codigo completo lo puedes encontrar Aqui El problema. He tenido la oportunidad de trabajar en distintos tipos de proyectos, algunos muy grandes, otros pequeños y otros de tamaño medio. En mucho de ellos he visto la implementacion de la capa de logica de negocios donde se defina una interfaz que contiene la definicion de las acciones de de ese servicio. Supongamos que tenemos una aplicacion que tiene la entidad cliente y en la capa donde vive nuestra logica de aplicacion tenemos una interfaz que define todas las acciones del cliente. Algo como esto. Dentro de esta interfaz podemos agregar cuantos metodos necesitemos y entre mas metodos agregemos, la clase que implemente dicha interfaz sera un mounstroo de muchisimas lineas de codigo dificil de leer. y de mantener. Esto no esta mal perse, ya que tenemos una interfaz que despues podemos inyectar en la capa superior que seria la capa de presentacion, nos permite hacere referencia a una abstracion, nos permitira tener mas facilidad de hacer unit testing y en general seguimos principios solid. Pero esto tambien es mejorable y aqui es donde viene una propuesta de solucion. Solucion. Partiendo la interfaz En la imagen anterior, la interfaz IClient implementa el principio de Single Resposanbility. Tenemos una interfaz que tiene muchos metodos de la logica de aplicacion pero aun podemos tener mayor granularidad. En lugar de tener una unica interfaz, podemos separarla en partes y generar una clase para cada accion, es decir, una clase por cada metodo definido en la interfaz. Cada una de estas clases hacen referencia a un case de uso enconcreto de nuestra capa de negocios y estan perfectamente separadas. Ahora, estas clases no implementan ninguna interfaz. Lo cual no esta mal por que aun asi podemos seguid inyectando cada una de estas clases en .NET pero no es lo ideal ya que estariamos dependiendo de una clase concreta y no de una abstraccion. Agregando IUseCase Para solucionarlo vamos a crear una interfaz que la nombraremos IUseCase y cada una de nuestras clases podras implementar esta interfaz. Ahora si, nuestro caso de uso ya puede ser inyectado de mejor manera en nuestro controlador y obtener el cliente por Id. Convirtiendo el IUseCase en generico. Bien, ahora con este diseño nos enfretamos a otro "problema". Y es que tenemos que crer una interfaz por cada caso de uso, definir los parametros que recibe y el tipo de la respuesta de cada metodo, ademas de eso el nombre que tendra el metodo que ejecutara la logica de aplicacion podria tener el nombre que sea. Para solucionar estos inconvenientes haremos uso de los Generics de C# y vamos a convertir a nuestra interfac IUseCase en una interfaz generica que tenga como entrada un tipo y que tenga una salida. Ademsa de todo esto, tambien vamos a definir otra interfaz IRequest que servira para marcar a los datos de entrada de nuesto Caso de uso. Con este caso de uso generico ya podemos estandarizar el nombre del metodo que ejecutara la logica de negocio en cada caso de uso, ademas, con la interfaz IRequest definimos que cada caso de uso tiene una entrada y genera una salida. La implementacion de el caso de uso de obtener un cliente por Id quedaria de la siguiente manera: Con esto, no necesitamos crear una interfaz para cada caso de uso, ahora estamos reutilizando una unica interfaz generica que nos ayudara a implementar la logica de negocio. Y para hacer uso del case de uso tendriams que inyectar la interface IUseCase de la siguiente manera: . Hasta aqui tenemos una separacion de la logica de negocios de nuestro controller y ademas cada caso de uso esta en una clase separada lo que hace que queca caso de uso tenga una resposabilidad unica. Seguimos manteniendo una inversion de control mediante la inyeccion de dependencias y segumos mantiendo la testeabilidad de la capa de negocios. Despachador de Casos de uso. Ok, considero que la implementacinon hasta este punto esta bien. Si la logica de negocio empieza a crecer en numero de casos de uso, entonces nos tocara injectar un monton de estos en el contolador, no es un lio pero quiza el codigo no se vea tan "limpio" con eso. Entonces, que tal que agregamos un despachador de casos de uso, es decir, una capa extra que internamente resuelva cual es el caso de uso que tiene que utilizar dependiendo de el request y el response: Esta seria la implementacion de nuestro use case dispatcher. Internamente hace uso del Service Provider de .net para poder entregarnos el caso de uso correcto. Ademas, podemos loguear errores cuando se ejecute el metodo ExecuteAsync de cada caso de uso. Y la implementacion en cada Controller seria algo asi:

Apr 9, 2025 - 22:04
 0
Separando la logica de la capa de negocios en casos de uso.

En este post quiero explicar como se puede lograr una separacion entre a logica de la aplicacion o capa de reglas de negocio del resto de las capas de la arquitectura que estemos utilizando.

El codigo completo lo puedes encontrar Aqui

El problema.

He tenido la oportunidad de trabajar en distintos tipos de proyectos, algunos muy grandes, otros pequeños y otros de tamaño medio.

En mucho de ellos he visto la implementacion de la capa de logica de negocios donde se defina una interfaz que contiene la definicion de las acciones de de ese servicio.

Supongamos que tenemos una aplicacion que tiene la entidad cliente y en la capa donde vive nuestra logica de aplicacion tenemos una interfaz que define todas las acciones del cliente.

Algo como esto.

IClientServiceInterface

Dentro de esta interfaz podemos agregar cuantos metodos necesitemos y entre mas metodos agregemos, la clase que implemente dicha interfaz sera un mounstroo de muchisimas lineas de codigo dificil de leer. y de mantener.

Esto no esta mal perse, ya que tenemos una interfaz que despues podemos inyectar en la capa superior que seria la capa de presentacion, nos permite hacere referencia a una abstracion, nos permitira tener mas facilidad de hacer unit testing y en general seguimos principios solid.

Client Controller with IClientService injected

Pero esto tambien es mejorable y aqui es donde viene una propuesta de solucion.

Solucion.

Partiendo la interfaz

En la imagen anterior, la interfaz IClient implementa el principio de Single Resposanbility.
Tenemos una interfaz que tiene muchos metodos de la logica de aplicacion pero aun podemos tener mayor granularidad.
En lugar de tener una unica interfaz, podemos separarla en partes y generar una clase para cada accion, es decir, una clase por cada metodo definido en la interfaz.

interfaces separeted

Cada una de estas clases hacen referencia a un case de uso enconcreto de nuestra capa de negocios y estan perfectamente separadas.

Ahora, estas clases no implementan ninguna interfaz. Lo cual no esta mal por que aun asi podemos seguid inyectando cada una de estas clases en .NET pero no es lo ideal ya que estariamos dependiendo de una clase concreta y no de una abstraccion.

Agregando IUseCase

Para solucionarlo vamos a crear una interfaz que la nombraremos IUseCase y cada una de nuestras clases podras implementar esta interfaz.

IUseCase

Ahora si, nuestro caso de uso ya puede ser inyectado de mejor manera en nuestro controlador y obtener el cliente por Id.

IUseCase In controller

Convirtiendo el IUseCase en generico.

Bien, ahora con este diseño nos enfretamos a otro "problema". Y es que tenemos que crer una interfaz por cada caso de uso, definir los parametros que recibe y el tipo de la respuesta de cada metodo, ademas de eso el nombre que tendra el metodo que ejecutara la logica de aplicacion podria tener el nombre que sea.

Para solucionar estos inconvenientes haremos uso de los Generics de C# y vamos a convertir a nuestra interfac IUseCase en una interfaz generica que tenga como entrada un tipo y que tenga una salida.

Ademsa de todo esto, tambien vamos a definir otra interfaz IRequest que servira para marcar a los datos de entrada de nuesto Caso de uso.

Image description

Con este caso de uso generico ya podemos estandarizar el nombre del metodo que ejecutara la logica de negocio en cada caso de uso, ademas, con la interfaz IRequest definimos que cada caso de uso tiene una entrada y genera una salida.

La implementacion de el caso de uso de obtener un cliente por Id quedaria de la siguiente manera:

Image description

Con esto, no necesitamos crear una interfaz para cada caso de uso, ahora estamos reutilizando una unica interfaz generica que nos ayudara a implementar la logica de negocio.

Y para hacer uso del case de uso tendriams que inyectar la interface IUseCase de la siguiente manera:

Image description.

Hasta aqui tenemos una separacion de la logica de negocios de nuestro controller y ademas cada caso de uso esta en una clase separada lo que hace que queca caso de uso tenga una resposabilidad unica.

Seguimos manteniendo una inversion de control mediante la inyeccion de dependencias y segumos mantiendo la testeabilidad de la capa de negocios.

Despachador de Casos de uso.

Ok, considero que la implementacinon hasta este punto esta bien.

Si la logica de negocio empieza a crecer en numero de casos de uso, entonces nos tocara injectar un monton de estos en el contolador, no es un lio pero quiza el codigo no se vea tan "limpio" con eso.

Entonces, que tal que agregamos un despachador de casos de uso, es decir, una capa extra que internamente resuelva cual es el caso de uso que tiene que utilizar dependiendo de el request y el response:

dispatcher

Esta seria la implementacion de nuestro use case dispatcher.

Internamente hace uso del Service Provider de .net para poder entregarnos el caso de uso correcto.

Ademas, podemos loguear errores cuando se ejecute el metodo ExecuteAsync de cada caso de uso.

Y la implementacion en cada Controller seria algo asi:

dispatcher in controller