Historia de una integración (iii): El desarrollo

El entorno de desarrollo estaba implantado, la arquitectura estaba definida, el análisis estaba en marcha. Era el momento de iniciar el desarrollo, antes de que el tiempo (y el cliente) se echara encima.

Pero pronto quedó claro que los servicios web SAP iban a tardar más de lo previsto. De hecho, ni siquiera disponiamos de los ficheros WSDL que describen el interfaz, que empezaron a llegar por cuenta gotas un par de meses después. Por supuesto, más de uno tuvo que ser modificado y remodificado. Esta situación nos obligó a ser extremedamente ágiles, cambiando la prioridad de los módulos, reconstruyendo los clientes webservices y modificando el modelo de la aplicación a menudo.

Metodología

No hemos seguido ninguna metodología concreta al pie de la letra. Aunque sí es cierto que nuestra forma de trabajar está ampliamente inspirada en Scrum y las prácticas ágiles que promueve. Sobre Scrum podeis leer más aquí y aquí.

Os dejo algunas notas de la estrategia que utilizamos:

  • Desarrollo incremental con una versión mayor por módulo. Empezando por el módulo de Administración.
  • Los requisitos fueron traducidos en nuestro JIRA a tareas de tipo New Request dentro de la versión correspondiente. Los cambios en los requisitos fueron añadidos como tareas de tipo Improvement. Desgraciadamente cuando la versión estaba cerrada no siempre se añadían como tareas hijas...
  • Simulación de los servicios web SAP mediante Mocks de los clientes webservices. Como ya he dicho los servicios no estaban, pero nosotros necesitabamos ya disponer de datos que pintar en las pantallas! Así que simplemente implementamos los interfaces de los servicios con unas clases tontas que devolvian datos más tontos aún. Mocking manual pero efectivo.
  • Testing de la capa Middleware. Toda la lógica de la aplicación está en nuestras clases service que son llamadas desde las pantallas mediante DWR. Los controladores web (Actions de Struts) practicamente sólo realizan navegación y precarga de campos de formularios. Focalizar el testing en la capa Middleware permitió independizarse totalmente de la capa Presentación y el servidor de aplicaciones, aumentando así la velocidad de desarrollo global.
  • Testing de la capa Acceso a base de datos. Para probar los distintos daos de esta capa usamos un script sql para inicializar la bbdd a un estado conocido junto con los tests transaccionales de Spring que automaticamente hacen rollback evitando la modificación del estado y la fácil configuración entre datasources dependientes e independientes del servidor de aplicaciones. Una más del montón de cosas que te facilita Spring.
  • Integración contínua. Básico en cualquier fase de test seria. Además de actuar como control de calidad de nuestro código. Pero de Hudson ya hablé en su día...
  • Estructura del proyecto. Para atajar la dificultad en un proyecto es básico tener una estructura inteligente y ordenada del mismo. Nuestra estructura de código fuente estaba formada por un gran paquete por módulo, más un paquete comun. Luego dentro de cada módulo, los subpaquetes web, dao, ws, modelo y services.
  • Tags, branches y merges. El cambio de prioridades entre módulos que sufrimos más el desarrollo incremental por módulos podrían habernos costado más de un disgusto serio sin una buena estrategia de control de versiones.

Dificultades

Hasta aquí parece que ha sido un paseo, pero en realidad hemos sufrido lo nuestro también. A parte del tema de la colaboración con el resto de equipos, tuvimos que hacer frente a varias dificultades en:

  • Integración mediante webservices. Se veía venir. Sinceramente e IMHO, los servicios web SAP están, como mucho, diseñados regularmente. Abundan los datos duplicados e innecesarios, no existen namespaces ni schemas comunes y aún nadie me ha podido explicar por qué existe un servicio web por cada operación! Además los WSDLs son generados por alguna herramienta SAP de forma automática que no cumple el WS-I. Lo que nos obligó a usar XMLBeans como OXM (Databinding Java-XML), aunque esto no tiene nada de malo, y también a tener que modificar a mano un par de WSDLs. Y luego está el tema de la infraestructura de red del cliente en producción y sus proxys, pero ésto también lo he contado ya.
  • Pruebas de los servicios web. Descubrimos muchos bugs en los servicios web SAP. Yo diría que sus equipos de desarrollo los probaban poco. Las modificaciones, a veces, ocasionaban nuevos bugs donde antes funcionaban. Pronto quedó claro que no tenían una fase seria de test. Al final tuvimos que programar nosotros las baterias de tests para los servicios web SAP.
  • Gestión de requisitos. Sabiamos que los requisitos iban a cambiar. Pero no tanto. El sistema de gestión de requisitos antes comentado no favorece el control de cambios en los requisitos, es demasiado frágil y requiere demasiado trabajo en mantener la descripción de la tarea inicial y las hijas, además de mezclarse aspectos de descripción de funcionalidad con pura implementación. En el futuro probaremos con una Wiki para los requisitos y el JIRA para las tareas exclusivamente con referencias al Wiki.
  • Formación. Inicialmente, y para mi sorpresa, el nivel de dominio del equipo en Spring e Hibernate era más bajo de lo esperado. Esto nos dió problemas especialmente en mapeos avanzados de Hibernate que provocaban que (1) algunas operaciones fallaran y (2) se realizaran más consultas de las necesarias. También destacar la dificultad, al principio, de trabajar con multiples ficheros de configuración Spring debido al número de módulos y las necesarias configuraciones para mocks y testing.
    Luego la cosa mejoro. Aunque a día de hoy Hibernate sigue sin convencer a más de uno pero con Spring todos han caido rendidos.
  • Proceso de sincronización. Todas las noches debe actualizarse una estructura de 7 tablas con PKs y FKs compuestas con casi 100 mil registros obtenidos mediante cientos de llamadas a 2 servicios web. Es una operación compleja y costosa, con o sin Hibernate. Fue necesario rediseñar los servicios web SAP para operaciones de consultas masivas que minimizaran el número de llamadas y optimizar los mapeos Hibernate así como los algoritmos usados. Al final pasamos de más de 1 hora de ejecución (tiempo inaceptable) a apenas 10 minutos.
  • Testing en la capa de presentación. En su momento no conseguimos hacer funcionar los tests funcionales web basados en Selenium en nuestro servidor de integración contínua, así que esta parte de test se hizo de forma manual. Como la aplicación era una aplicación web RIA bastante grande que tenía que funcionar en IE6, sumado a los cambios de requisitos y modificaciones en los servicios web, os podeis imaginar lo que hemos pasado...

Creo que no me dejo nada. Los temas de colaboración e implantación proximamente...

2 comentarios :: Historia de una integración (iii): El desarrollo

  1. En "Estructura del proyecto" dices: "que las fuentes estan formadas por un gran paquete por módulo, más un paquete comun. Luego dentro de cada módulo, los subpaquetes web, dao, ws, modelo y services.".
    Y si divido la aplicacion por tres grandes paquetes: web, service y dao?. y dentro de cada uno de estos paquetes los separo por modulo. Cual seria mejor, o que diferencias habria. O es indistinto usar una u otra estructura de directorios??... gracias por la rpta

  2. La diferencia la marca quien tiene más importancia, si el módulo o la capa. En este caso son los módulos que son independientes (o casi). Las funcionalidades van sobre módulos y luego desciendes a la capa.
    Es más cómodo si tienes que hacer algo, descender primero al módulo y luego a la capa. Así directamente descartas una buena parte del arbol de directorios.

    Podría darse el caso contrario, aunque no es lo habitual.

Publicar un comentario