Validaciones y manejo de errores en la ui

De Uqbar wiki

Introducción

Una cuestión en la que hay que pensar es dónde poner las validaciones. Tenemos validaciones en la interfaz de usuario (o en el binding) y en el dominio. Recalcamos la importancia de poner en el dominio las validaciones de negocio, en los demás lugares deberían ir únicamente las validaciones propias de la interfaz de usuario, por ejemplo las conversiones de datos que requiere la interfaz.

Algunos ejemplos:

Interacción entre dominio y vista para manejar validaciones de negocio

Una vez que tenemos validaciones que elegimos colocar en el dominio debemos pensar en qué forma el dominio informa los problemas a la interfaz de usuario, siendo que debe hacerlo en términos del dominio.

>>>Empleado
public void validar() {
   ...
   if (!new Date().before(this.fechaNacimiento)) {
       throw new UserException("La fecha de nacimiento tiene que ser anterior al día de hoy");
   }
   ...
}

Para manejar las excepciones suele ser útil tener una excepción que sea base de todas las excepciones que contienen información para el usuario (en nuestro ejemplo la llamamos UserException). De esta forma siempre que la UI reciba una excepción de este tipo sabe que debe mostrar el mensaje de error al usuario y cualquier objeto de dominio que necesite informar un error lo puede hacer tirando una excepción de este tipo o alguna subclase. De esa manera la UI no conoce nada de la lógica que maneja el dominio y mantenemos bajo el acoplamiento entre ambos componentes.

Errores de sistema

¿Qué sucede si hay un error de programa?

Pero ¿dónde? Una buena regla es poner try/catch en los lugares en los que, de otra manera, la aplicación mostraría el stack trace al usuario. Nosotros no queremos que eso suceda, pero es preferible que el usuario llame por una pantalla rota a que el error quede escondido, como en estos típicos casos:

>>>Código de pantalla
public void buscarSocios() {
   try {
       ... hacemos la búsqueda ...
   } catch (Exception e) {
       e.printStackTrace();
   }
}

El usuario no va a reportar que la búsqueda tira error, sino que “ciertas” búsquedas no funcionan. A veces trae datos y a veces no. De esa manera la aplicación pierde robustez.

Otro error común en el manejo de errores es encerrar sólo parte del código en un bloque try/catch (la instrucción que sabemos que puede fallar), loguear el error o incluso no manejarlo y permitir que el método siga ejecutándose, como lo muestra el siguiente ejemplo:

>>>Código de pantalla
public void buscarSocios() {
   Socio socio = null;
   try {
       ... hacemos la búsqueda ...
   } catch (ProgramException e) {
       e.printStackTrace();
   }
   // envío mensajes al socio que puede no estar correctamente inicializado
   if (socio.getAntiguedad() > 2) {
      ...
   }
   ...
   ...
}

Tenemos dos problemas que resolver:

  1. La búsqueda tira una ProgramException
  2. Pero el error que vamos a recibir es NullPointerException cuando se envíe el mensaje getAntiguedad() al socio

Vamos a buscar el problema en getAntiguedad() para luego darnos cuenta de que en realidad lo que falló fue la búsqueda. Si el sistema está bien construido, sabe decir dónde falló a tiempo: es consistente.

¿Qué va en el catch del lado de la pantalla?

Por un lado, hay que guardar el error en un archivo de log dentro del servidor donde corre la aplicación en algún formato amigable que nos permita encontrarlo rápidamente. Para ello existen algunas librerías como Log4J y loggers alternativos que ayudan a

Por otra parte, necesitamos informar al usuario que cuando estábamos buscando los socios, cuando queríamos agregar el socio, cuando estábamos inicializando la pantalla, etc. ocurrió un error y la aplicación no va a poder responder correctamente el pedido. Y eso depende mucho de la tecnología en la que nos encontramos.

Un ejemplo genérico de manejo de errores en UI

>>>Código de pantalla
public void agregarSocio() {
   try {
       Socio socio = ... recolectamos la información de la UI ...
       socio.validar();
       ... pedimos que generen el socio, esto depende de nuestra estrategia ...
   } catch (UserException e) {
       this.messageWarning(e.getMessage());
   } catch (Exception e) {
       this.logger.fatal("AppSocios", e.getMessage());
       this.messageError("Ocurrió un error al agregar el socio. Por favor consulte al administrador del sistema");
   }
}

Los métodos messageWarning y messageError, como dijimos antes, dependen de la tecnología a implementar, en nuestro caso asumimos que está en una superclase de Form de la cual hereda la pantalla de Alta de un Socio.

El logger es un objeto que sabe guardar el stack trace en un formato configurable y en un archivo x que depende del primer parámetro que le paso (por ejemplo, el AppSocios guarda el archivo Videoclub.log en el directorio de log default).

Otras variantes

Un agregado que se podría hacer es evitar poner los mensajes de error en el código de dominio y poner códigos, que se traduzcan mediante un “bundle”.

Links relacionados

Latest update on July 17, 2017 by GitHub