
Beneficios del Patrón de Resultado
Cuando costruimos servicios o APIs, un punto común a discutir es sobre como usar los códigos de estado de respuesta HTTP para comunicarle a los consumidores o clientes lo que está pasando por lado del servidor al manejar su petición de datos o sus solicitudes de ingreso de datos.
Si no estás familiarizado con los códigos de estado de respuesta HTTP te recomienda leer la pópular página de la fundación Mozilla, que es referente sobre este tema:
HTTP response status codes - HTTP | MDN (mozilla.org)
A menudo suelo ver código de aplicación que usa excepciones para realizar validaciones de negocio y para controlar el flujo de negocios. Estas excepciones lanzadas voluntariamente suelden terminar como un código 4xx o en el peor escenario en códigos 5xx. Hasta este punto es valido preguntarse ¿Cuál es el problema en hacerlo de esta forma?
Como desarrolladores solemos enfocarnos en cumplir con los requerimientos que nos fueron encargados, cumplir con las historias de usuario o tareas que nos asignaron. Hacemos nuestro mayor esfuerzo en que el código que escribimos implemente la lógica de negocio con la mayor precisión posible para poder pasar a la siguiente tarea. Es decir nos enfoncamos en cumplir los requerimientos funcionales y de los stakeholders de negocio.
Pero, ¿Qué pasa con los stakeholders técnicos y los requerimientos no funcionales? Para el tema que estamos tratando me voy a enfocar en un stakeholder que ha tomado mucha relevancia, un ingeniero de confiabilidad de sitio o por sus siglas en ingles - SRE. Unos de los requisitos no funcionales que suele plantearse un SRE es poder medir la disponibilidad de una aplicación o sistema informático. Existen muchos planteamientos para medir la disponibilidad, pero uno de los más comunes es la siguiente fórmula:
disponibilidad = Número de peticiones con error / Total de peticiones HTTP
En esta forma de medir la disponibilidad el “Número de peticiones con error” suele plantearse como la suma de las peticiones que devuelven códigos de estado 5xx y también las que devuelven código 4xx. Es aquí donde podemos contestar la pregunta ¿Cuál es el problema en utilizar excepciones para hacer validaciones y control de flujo de negocio?
El punto es el siguiente, si el código del servidor esta haciendo un trabajo correcto en hacer las validaciones de negocio requeridas y no tuvo ningún problema inesperado ¿Por qué desencadenar una excepción? Lanzar una excepción es algo técnicamente factible, pero en terminos conceptuales una excepción debe ser un recurso utilizado para situciones no esperadas y que escapan del control del código de la aplicación.
Si las validaciones de negocio y el control del flujo de negocio se realizan con excepciones y estas se reflejan como resultado en códigos 5xx y 4xx, están impactando negativamente en el cálculo de la disponibilidad.
Algunos podrán argumentar, bien entiendo que devolver códigos 5xx es algo extremo para validaciones de negocio pero no veo problema en devolver 4xx. La clave está en diferenciar “validaciones de negocio” de “validaciones de datos o de entradas”, las primeras estan orientadas a resolver casuistica de negocio que puede llegar a se compleja y representa la esencia del dominio de nuestra aplicación, mientras las segundas solo deben contemplar la validación de los “inputs” de los consumidores. En términos de arquitectura de aplicaciones las primeras llegarían a nuestra capa de negocio, mientras las segundas pueden ser gestionadas por el framework o por una librería utilitaria, sin llegar a la capa de negocio.
Realizado el análisis, ¿Cuáles son los beneficios del patrón de resultado? El patrón de resultado puede ayudarte con lo siguiente:
- Permite establecer una diferencia clara entre validaciones de negocio y validaciones de datos
- Permite delimitar el alcance del código de dominio del código para validar inputs, lo cual puede decantar en mejor legibilidad del código
- Ayuda a la depuración, ya que el objeto resultado puede contener información del problema de negocio, lo que facilita la detección de problemas, sobre todo a consumidores
- Coherencia en las respuestas de API, lo que facilita la integración para los consumidores
- Indirectamente puede conllevar un menor consumo de memoria. En una implementación poco madura los desarrolladores intentarán acceder a los objetos de traza de la excepción para luego escribirlos como log de error, esta simple acción puede significar un consumo de memoria elevado, dependiendo del nivel de recursividad de los objetos de traza
El siguiente diagrama puede ilustrar mejor como se vería una implementación del patrón de resultado:

Puede que en este punto te realices la siguiente pregunta:
¿Como gestiono las respuestas de APIs externas que no siguen el patrón de resultado, pero de las cuales soy consumidor?
Una manera de alinear las respuestas de estas APIs externas es usando el patrón BFF. El backend de canal servirá como un proxy para invocar a tus APIs externas, de esta manera podras manejar o reinterpretar los códigos de estas APIs de acuerdo a tus propias reglas y definiciones de negocio, consiguiendo con esto implementar el patrón de resultado ante tus clientes finales.
Si aún no estas familiarizado con el patrón BFF te invito a leer mis artículos relacionados:
¿Estás usando todo el potencial del patrón BFF?
¿Qué pasa con las aplicaciones móviles y BFF?
Veamos ahora una posible implementación del patrón de resultado con mi stack favorito, .NET y lenguaje C#.
Para la capa de aplicación o presentación se puede tener el siguiente código: Este bloque simple implementa .NET Minimal API para habilitar endpoints, utiliza una implementación utilitaria de validación para los inputs, que devolverá Bad Request si los inputs no cumplen con las especificaciones requeridas.

Para la capa de dominio o de negocio se pue tener el siguiente código: Se usa el patrón de separación de responsabilidad, CQRS, por eso se crea un comando de creación de producto, en el metodo Handle se deben realizar las validaciones de nuestras casuisticas de negocio y retornar un objeto Result que representa el resultado de nuestra operación.

Para finalizar la clase de resultado, que es uno de los ejes de la implementación del patrón de resultado:

Sígueme en Linkedin para estar al tanto de mis publicaciones y novedades