Introducir result types en un proyecto vivo

Wei-Yu

Ahora mismo me veo en una situación en la que creo que puedo introducir al proyecto en el que estoy un manejo de errores y control de flujo un poquillo más limpio que devolver null y pasar por referencia strings errorMessage (xd). Para poneros en contexto, la aplicación es un monolito .net (C#) a capas (MVC) que a efectos prácticos se limita a exponer una variedad de APIs de muchos colores y sabores distintos.

Mi idea era poder representar de forma genérica el estado de una petición http o un flujo interno de la aplicación, de forma que quede clara la dicotomía entre consumo externo/interno y teniendo en mente que puede darse la situación de que un estado de éxito pueda ser parcial y acarrear mensajes de error, que no es algo muy común pero viendo el percal me veo que van a depender de muchos "servicios externos". Siempre desde la perspectiva de usarlo en desarrollos nuevos o modificaciones que cambien de forma drástica la interfaz que se expone al exterior.

El caso es que no me veo cómodo con la idea y vengo a que me tiréis unos machetes a ver.

En un principio pensé en tener dos clases para tipificar los flujos, un Result para "operaciones internas" y un Response con el que exponer el resultado "final". En código la clase Result sería algo así:

spoiler

Utilizo un booleano Success para ahorrarme comparativas con valores vacíos; a través del estático Result<T>.OK(T value) se construye un Result con Success positivo (porque default(T)/null pueden ser estados finales de éxito). La instanciación la hago a través de métodos públicos estáticos para tener margen para poder dejar el objeto en un estado "predefinido" de éxito o fracaso; no quiero que el resultado no sea algo final en ningún momento.

La clase Response a grandes rasgos es lo mismo, sólo que incluye una lista de tipos Error, también de sólo lectura. El overload de estáticos es por comodidad de quien lo vaya a usar más que nada. Pego aquí el código por completitud:

spoiler

Y este sería el de la clase Error:

spoiler

Y esto serían unos ejemplos tontos de uso:

spoiler

¿Opiniones?

  • follow-up: mañana tengo pensado hablar sobre cómo poder expresar estados de error que consumir de forma programática a ver si llegamos a un consenso. Me gustaría representarlo con integers pero no sé si es algo que castrará a la gente en un futuro o será suficientemente expresivo. Pensé en identificadores tirando de dos-tres siglas+numerito rollo "MV08", pero tampoco me acaba de gustar.
Ranthas

#1 Después lo leo más en profundidad y te doy una respuesta en detalle.

En nuestra empresa colaboramos con un tercero que expone un servicio (API) que actua como un proxy, que a su vez consume métodos de una pléyade de API.

El proxy, facade o como cojones lo quieras llamar expone dos métodos: login, que te devuelve un token, y un doOperation, que recibe como input el token anteriormente mencionado, y un XML que contiene que la peticion a la API pertinente.

Cuando nosotros, desde nuestras aplicaciones tenemos que consumir esa asquerosidad, usamos una API intermedia nuestra que actua como gateway, y como en tu caso, hay veces que esa API nos devolvía un 200, pero la petición a la API "escondida" había fallado.

¿Nuestra solución? Al consumir las APIs detrás del proxy, nos llega siempre una respuesta de success/failure, y lo que hacemos es parsear esa respuesta. En función del error, lanzamos la excepción pertinente con un http.errorcode=400 si el fallo proviene del usuario, o un 422 si es un fallo interno de esa API, y que nosotros no podemos subsanar.

Si esas APIs de distintos colores y sabores devuelven siempre el mismo dto, este es el camino, como dirían los mandalorianos. Si por el contrario, te devuelven respuestas de distintos colores y sabores, tu idea la veo bien; un contra que le veo es que si este servicio sigue creciendo, el tema de los identificadores va a ser un puto infierno.

1
r2d2rigo

Ni se te ocurra soportar partial success porque es un clusterfuck a todos los niveles. Una operacion o se completa o falla, no hay medias tintas.

No sobreingenierices con generics desde el principio porque estoy segurisimo que se va a dar el caso en el que un mismo endpoint devuelva dos tipos distintos y a ver como lo distingues con la estructura que tienes ahora.

No estas haciendo null check de errors antes de AsEnumerable.

Si la lista a a ser de solo lectura usa IReadOnlyList.

Ah, y con menos buzzwords las explicaciones se entienden mejor.

2 1 respuesta
Wei-Yu

#3 la idea es que la gente implemente el tipo, no que tire de generics dentro del código; sólo es un contenedor que poder reutilizar.

supongo que si el resultado "es parcial" debería manejarlo yo en la parte más externa en vez de tener ese mecanismo dentro de la clase; tampoco me gustaba la idea del todo cuando lo pensé pero no soy capaz de extrapolar el porqué, así que a ver si leo algo que me ayude a que cuaje

p.d: se aceptan sinónimos para las buzzwords shrug

Usuarios habituales

  • Wei-Yu
  • r2d2rigo
  • Ranthas