Programando mantenimientos

Estaba escribiendo un post sobre mis primeros pasos con Aurelia y escribiendo de lo que quería hacer me ha salido este post extra. Así que haya voy.

Cuando empiezas una aplicación nueva siempre se pierde mucho tiempo en empezar. Yo lo que hago es empezar a hacer CRUDs. Yo los llamo mantenimientos.

Para mí, las aplicaciones, en cuanto a la capa de presentación se refiere, tienen dos partes claramente diferenciadas, las pantallas de mantenimiento de entidades, los CRUD; y las pantallas de procesos. Los mantenimientos suponen un 85% de las interfaces de usuario y los procesos el otro 15%. Los procesos dependerán siempre de la aplicación en cuestión y, aunque se pueden automatizar un poco, son algo que toca hacerte desde cero, son esas tres o cuatro pantallas donde el usuario va a tirarse más tiempo y es ahí donde debes echar el resto, es ahí donde se va a valorar tu aplicación. Tu tiempo, tus esfuerzos deben ser destinados a esa parte. Digamos que esa parte es en realidad donde tienes que trabajar. Pero claro esos procesos deben ser alimentados con datos y esos datos deben entrar por algún sitio. Para ello tendrás que tener una mínima infraestructura para manipular dichos datos y deberás de brindar una serie de pantallas para dar la posibilidad al usuario de manipular dichos datos.

Las pantallas de mantenimiento son siempre iguales, de hecho, para el usuario, es mejor que sean iguales, así entendida una entendidas todas. Grosso modo las pantallas de mantenimiento deben mostrar la lista de entidades que mantiene, mostrando por cada una los datos más relevantes. Deben permitir filtrar las entidades de la lista por distintos campos. Deben permitir crear nuevas entidades al igual que eliminarlas. También poder editar los datos de cada entidad. Y por último poder realizar acciones determinadas con cada una de ellas o con un grupo. Por lo tanto, si todas las pantallas de mantenimiento son iguales, ¿por qué programar cada una de ellas?

Bajo mi punto de vista para completar un CRUD, una pantalla de mantenimiento, se debe especificar tan solo:

  1. Campos por los que filtras.
  2. Campos a mostrar/modificar en la edición.
  3. Campos necesarios para la creación de la entidad, que en el 70% de los casos, si no es el 90%, son los mismos que en la edición.
  4. Campos a mostrar en la lista.
  5. Opciones especiales de la entidad.

Ya está, la creación, la edición, el borrado, la copia y todo lo demás derivado del mantenimiento de entidades debe ser código genérico.

Para esto, concluí que para crear un crud debía de crear 4 o 5 plantillas. Dado que últimamente programo todo en web, o más bien en html + javascript las plantillas suelen ser html. Las plantillas son: los inputs del filtrado, los inputs de la edición, los inputs de la creación, siempre y cuando no reaproveches los de edición, un div con la información de la entidad para la lista de entidades y un div con los botones adicionales para el tipo de entidad en cuestión.

Por lo tanto debemos tener un objeto, componente, clase o un algo, que sea capaz de orquestar un mantenimiento de entidades con esas 5 plantillas. Se suele tener un objeto CRUD que coja dichas plantillas y sea capaz de montarte la pantalla.

En mi caso mi componente mantenimiento recibe el nombre de la entidad, por ejemplo Product y con eso carga del servidor una vista que contiene cada una de las plantillas. Por defecto llama al servidor a /View/Templates/[nombre-entidad] para obtenerlas. Una vez tiene las plantillas lo que hace es especificar el título de la página con Mantenimiento de [nombre-entidad] luego muestra al inicio la plantilla de filtrado y a continuación muestra la la lista de entidades. La lista de entidades la obtiene en formato json llamando al servidor con /Api/Product/List paséndole un objeto de filtro. Las entidades las muestra en una lista y cuando pulsas en una de ellas aparece un menú contextual con las opciones disponibles para esa entidad, es ahí donde inserta la plantilla de las opciones especiales de la entidad, justo después de las opciones genéricas de editar, copiar y eliminar.

Cuando pulsamos sobre el botón de editar se abre una pantalla con la plantilla de edición rellena con los datos de la entidad que se quiso editar. Si se pulsa eliminar se pregunta si esta seguro y elimina, y si le das a copiar pues abre la pantalla de creación con la plantilla de creación rellenando los datos con la entidad seleccionada. Y bueno, … todo así.

El componente por debajo, en el código, debe tener la lista de entidades mostradas en pantalla en un array, la entidad actual, la seleccionada en la lista, y el objeto de filtrado que se envía cada vez que consultamos la lista de entidades. Por último deberá tener el código necesario para la creación, edición y eliminación de entidades, así como las llamadas al servidor, que siempre se construyen de la misma forma:

  1. Buscar /api/[nombre-entidad]/list + el filtro.
  2. Crear /api/[nombre-entidad]/create + los datos necesarios para crear.
  3. Guardar /api/[nombre-entidad]/save + los datos cambiados.
  4. Eliminar /api/[nombre-entidad]/delete + el identificador de lo que se quiere borrar.

Estas llamadas se pueden hacer usando RESTful o no, yo por ejemplo uso post para todo y devuelvo un resultado como objeto, pero se puede hacer perfectamente con RESTful.

Por supuesto que aunque todo este código sea siempre genérico, en muchas ocasiones necesitarás “meterte por en medio”, es decir, siempre aparecen cosas como… cuando se inserta un albarán se debe recalcular el stock, cuando se crea un ticket hay que descontar en el almacén, cuando se modifica un producto no se pude cambiar el SKU,… para eso debemos implementar las cosas de forma que sea fácil poder reescribir estos comportamientos genéricos. Tanto en el servidor como en el cliente, no vale poner el campo a readonly y luego no chequearlo en el servidor “cazurro!!” 😀

Y bueno, se podría hablar mucho sobre este tema, pero de momento me ha servido para plasmar algo que siempre he llevado en la cabeza y de lo que no he visto escribir mucho en español.

6 comments

  • Eso es correcto para entidades simples. En el momento que la entidad es más compleja, aparecen múltiples excepciones que hacen que ese código genérico se tenga que matizar en múltiples puntos y la interface de usuario no pueda ser construida dinámicamente.

  • Si Juan, hay entidades más complejas, yo las dividiría en dos:

    1. Las que son tan complejas que las pantallas no son crud, por lo tanto estas pertenecen al 15% que son procesos.

    2. Entidades que son complejas por que tienen diversos datos asociados que hacen complejo el manejo de las mismas. Para estas lo que se hace es definir objetos DTO y la interfaz trabaja con ellos y el servidor se encarga de transformar esos DTO en entidades.

    Pero lo que es el crud es exactamente lo mismo para todas, luego para unas u otras, habrá que interceptar algunas acciones para hacerlas a mano, pero al final todo es lo mismo, recupero objetos del servidor, los muestro, edito un objeto y lo mando al servidor, creo un objeto y lo mando al servidor, llamo al servidor a realizar una acción sobre un objeto enviando el identificador y los valores para dicha acción, … y así sucesivamente.

    Un abrazo!

  • Pingback: Se busca Framework – Angular 2 no me convence. | 0 errors, 0 warnings

  • Pingback: Se busca framework – Aurelia | 0 errors, 0 warnings

  • Miguel Caravantes

    Me podrías comentar el porque usas post para todo me interesa porque IE tiende a dejar en caché mis gets y cuando actualizo la lista de un mantenimiento al que se le acaba de agregar uno nuevo puede que no se ve reflejado en la lista. En Angular agregé unas cositas para que esto no ocurriera pero me gustaría conocer la opinion de solo usar POST

  • Hola Miguel. Me da rabia no poder contestarte con más exactitud ya que llevo tiempo intentando probar temas de cache y no he podido hacerlo todavía…

    En principio uso mi idea era usar GET, pero la razón para cambiarme a usar POST es poder pasar parámetros en el body del request, o como dice mi amigo Sergio León, en el payload. Al pasar los parámetros de este modo puedo pasar estructuras de objetos más compleja usando content-type: application/json.

    En cuanto al tema del caché, como te decía, no me he puesto bien a probarlo todavía, pero si que he consguido anular las caches del navegador desde el servidor, en ASP.NET MVC usando el attributo [OutputCache]. Por lo tanto entiendo que no será toda la culpa del navegador, entiendo que cuando se usa [OutputCache] en el servidor, este meterá alguna cabecera en el response que indicará al navegador que no cachee la respuesta o que la cachee por un determinado tiempo. El tema es probarlo, hacer una petición y ver la respuesta, luego en el servidor indicar que no haga caché y volver a hacer la petición; entonces vemos que ha cambiado y tenemos la respuesta al problema. Si lo pruebas y te acuerdas no estaría mal tener la respuesta exacta.

    Saludos,

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

Demuestra que no eres un bot *