Feeds:
Entradas
Comentarios

IronHack


Hace unos tres meses, en la fiesta final de la SpainJS conocí a Mauricio. Una persona muy entusiasta y con ganas de llevar a la realidad su último proyecto. Estuvimos hablando un rato y nos despedimos. Hace un par de semanas, ya desde Berlín, me sorprendió ver que este proyecto había pasado de sueño a realidad. Sí, me sorprendí, porque no es usual que los proyectos se transformen en realidades, y eso habla bien de Mauricio, y de su tesón trabajando.

Así que en este post voy a hablar de IronHack, ¿por qué? Ellos no me pagan un duro por este post, pero pienso que es una iniciativa muy interesante, en especial para la comunidad de jóvenes desarrolladores. De hecho yo mismo estuve a punto de involucrarme en un par de iniciativas similares que desgraciadamente no consiguieron despegar.

(Enrique, déjate de rollos y ve al grano, que luego la gente mira el scroll y huyen espantados)

¿Qué es IronHack? Es sencillo, es una escuela para nuevos programadores donde se enseña como hacer software en el mundo real. Imagínate, hace poco que has terminado la universidad o estas terminando. Ha sido duro, pero lo peor es que la mayoría de los conocimientos que te han enseñado son obsoletos. Tal vez sepa algo de JAVA y SQL, quizás teoría de compiladores e incluso C. Con suerte entenderás el modelo waterfall de desarrollo de software y si os han hablado de Scrum ya tendréis más suerte que yo. En mis tiempos me enseñaron el modelo COCOMO, y ¿quién se acuerda de eso? No se vosotros, pero a mi cuando me enseñaron OO me decían que era como un modelo entidad relación pero con herencia.

Sí, tal vez encontraras trabajo en una consultora «cárnica» con todo eso, pero no en una startup o en una empresa importante que se dedique a hacer software de verdad. Hay cosas realmente importante que desgraciadamente no se están enseñando en la universidad: como Ruby o JS, como TDD y BDD, los principios de «clean code» y SOLID, HTML y CSS, Git y GitHub y como poner una aplicación en producción en la nube. Si piensas que todo esto son «moderneces», os recuerdo que a mi me preguntaron sobre todo esto de forma exhaustiva cuando me entrevistaron para eBay.

No sólo es el temario, sino el método didáctico y los profesores. No es lo mismo que te enseñen todo esto un teórico que no ha sacado a producción software en su vida, a que te lo enseñe un aguerrido veterano del mundo real.  Echad un vistazo al listado de personas involucradas en el proyecto. Y por supuesto, aprender haciendo. Se trata de aplicar los conceptos a un pequeño proyecto y ponerlo en producción.

Finalmente está la guinda, tal vez alguna de las empresas patrocinadores contrate a los mejores de la promoción. Las empresas no son tontas y saben que los medios tradicionales de reclutamiento sólo consiguen gastar dinero y reclutar no-talento.

Si alguno está buscando diferenciarse del resto de sus compañeros de promoción, o simplemente aprender cosas nuevas. Echadle un vistazo a IronHack, tal vez os interese.


Hola a todos, en este post no voy a explicar nada técnico, sino que voy a hacer publicidad de una iniciativa privada y capitalista: mis cursos de formación.  A partir de ahora, si veis un post de «Ganarse el pan» que sepáis que es un anuncio comercial.

Hasta ahora he estado haciendo formación, consultoría y mentoring para empresas. La verdad es que no me ha ido mal, pero siempre me quedó el gusanillo de abrir la formación a individuos particulares. Así que decidí organizar una serie de cursos  sobre JS. Tras unos iniciales intentos fallidos, ya por fin tenemos el primero: «JS Profesional». El titulo, lo confieso, es un poco serio, y barajé algún título más escandaloso, pero al final lo he dejado en una cosa más neutral. Se trata de un curso orientado al típico desarrollador  que nunca ha trabajado en serio con JS, sino que sólo ha tenido un contacto superficial del lenguaje, como validación de formularios, efectitos con jQuery y un poquito de AJAX. La mayoría de los desarrolladores de «server side» están en esta tesitura. El objetivo del curso es dar un empujón a estos profesionales, para que JS deje de ser un lenguaje misterioso que falla incomprensiblemente. Son 10h de curso, pero van a ser muy intensas, y te van a dar la base para empezar a practicar JS a otro nivel. Eso sí, después hay que practicar en la vida diaria lo que se ha aprendido en el curso. Para más información sobre el curso visita aquí: https://eiar.stagehq.com/events/1683

Pero mis planes no pasan sólo por este curso, quiero crear una serie de cursos sobre temas avanzados: Node.js, REST con JS, integración continua con JS, etc.

Todos los cursos van a seguir el mismo formato: dos clases de 5h cada una. En principio vamos a hacerlo los viernes por la tarde y los sábados por la mañana. La idea es que sea un horario compatible con la gente que trabaja. Sin embargo, he recibido peticiones de poner cursos en otro horario, así que aquí os dejo una encuesta sobre el tema:

Si estas desempleado, no creo que te importe mucho el horario, pero en cada curso reservamos una plaza gratuita para desempleados. Con esto queremos unirnos a la iniciativa #DecisionesQueAyudan. Simplemente tienes que mandarnos tu CV, y justificante de estar desempleado, a nuestra cuenta de correo: info.eiar@gmail.com

Y finalmente os quiero comentar que no voy a estar dando estos cursos yo sólo: me he asociado con el amigo Israel Alcázar El mismo lo explica en su blog En principio Israel va a hacer un curso sobre Git (sistema de control de versiones que todo el mundo debería estar usando ya, excepto si está usando Mercurial o el magnífico PlasticSCM). Más información sobre este curso aquí. Lo interesante es que como estamos juntos en esto proponemos un descuento de 10 euros por curso si te apuntas a ambos cursos. Si te apuntas a un curso mío, recibirás por correo un cupón de 20 euros de descuento para el curso de Israel, y viceversa.

Para finalizar otra encuesta, esta vez sobre el tema precio:

Y por último agradecer a toda la gente que ha hecho retuits y ha recomendado el curso, con gente como vosotros todo es más sencillo.

Bueno, espero que esta iniciativa tenga éxito y podamos seguir dando muchos cursos!


¡ Bienvenidos de nuevo al mundo de CQRS ! Hoy nos vamos a centrar en ver como podemos implementar la persistencia del subsistema de comandos. La opción más tradicional es persistir una instantánea del modelo de datos directamente en una base de datos relacional mediante un ORM o similar. Los amantes de noSQL querrán grabar dicha instantánea usando sus sistema noSQL favorito. Pero ambas opciones se basan en grabar una instantánea del estado del modelo de datos. Sin embargo existe un sistema más simple y que aprovecha el ya existente mecanismo de eventos. Me estoy refiriendo a Event Sourcing.

Concepto y motivación

Ya hemos decidido que el subsistema de comandos va a emitir eventos de negocio, lo cuales serán procesados por el subsistema de consultas para actualizar su esquema de datos interno. Esto tiene algunas consecuencias cruciales:

  1. El subsistema de comandos no va a ser nunca leído por los clientes del sistema, ya que ellos usan siempre el subsistema de consulta.
  2. El subsistema de consultas es el único cliente que realiza operaciones de lectura sobre el subsistema de comandos.
  3. Lo único que va a pedir el subsistema de consultas al de comandos es la lista de eventos de negocio ocurridos desde la última vez que se produjo una sincronización.
  4. Si ocurre un desastre y perdemos todos los datos del subsistema de consultas, basta con levantarlo sin datos, y hacer que le pida al subsistema de comandos todos los eventos ocurridos desde el principio. Por lo tanto el estado del sistema puede residir por entero en el subsistema de comandos. Más sobre esto en futuros posts.

Visto esto, ¿para qué vamos a molestarnos en persistir un esquema de datos basado en entidades y relaciones? Al fin y al cabo ni los clientes ni el subsistema de consulta están interesados en estas entidades. Además lo único que necesitamos para recuperar el sistema en caso de desastre es la lista de eventos de negocio. Por lo tanto lo único que debería ser persistente es la lista de dichos eventos y no un hipotético modelo entidad/relación o un modelo de objetos.

Y este es precisamente el famoso Event Sourcing: almacenar todo el estado del sistema únicamente como una secuencia ordenada de eventos. Por lo tanto si nos decidimos por un mecanismo de sincronización basado en eventos de negocio, el enfoque de Event Sourcing a la persistencia es muy natural. No necesitamos almacenar entidades, ni las relaciones entre ellas, sino serializar una lista de eventos. Cada evento puede poseer campos, pero nunca vamos a necesitar buscar eventos por dichos campos, sino sólo por orden temporal, o como mucho por tipo de evento. El subsistema de consultas sólo va a realizar accesos como el que sigue: «dame todos los eventos sobre pedidos desde el momento X hasta la actualidad por orden cronológico», o «todos los eventos desde el principio».

Otra ventaja de event sourcing es que tenemos todo el historial de operaciones del sistema, lo que lo hace ideal para auditorias, depuración e informática forense.

No digo que debamos usar siempre event sourcing, pero siempre y cuando usemos eventos de negocio como mecanismo de sincronización, me cuesta trabajo pensar en escenarios donde event sourcing no tenga ventajas sobre grabar el modelo tal cual.

Transaccionalidad y consistencia

A nivel transaccional es bastante sencillo: cada comando es una transacción, cada transacción termina con la generación de un evento. A nivel de consistencia podemos tener varios niveles, en función de cuando el comando (la transacción) se da por terminado:

  • Fin de transacción cuando el evento se genera en memoria. Tanto la persistencia del evento como su transmisión al subsistema de consultas se produce de forma asíncrona, en segundo plano. Esto nos da el máximo de escalabilidad y rendimiento. Pero la consistencia es eventual y  podemos perder eventos (durabilidad baja).
  • Fin de transacción cuando el evento se persiste. La sincronización se produce de forma asíncrona, en segundo plano. De esta forma no perdemos eventos, a costa de tener una consistencia eventual entre las consultas y los comandos. Sin embargo la escalabilidad es bastante buena, a condición de que las escrituras sean rápidas. Es un buen compromiso como norma general.
  • Fin de transacción cuando el evento se persiste, y además el subsistema de consultas ha sido actualizado. Aquí tenemos consistencia estricta y durabilidad, pero penalizamos el rendimiento de las escrituras. Si por cualquier caso el subsistema de consulta está muy estresado, podemos perder disponibilidad en la escritura. Si realmente te encuentras en un escenario que requiere una consistencia tan estricta como esta, tal vez no deberías estar usando ni event sourcing ni CQRS.

Como vemos debemos usar un mecanismo de persistencia que escale muy bien a nivel de escritura (excepto en el primer caso) para que nuestro subsistema de comandos escale. No es tan importante en este caso la escalabilidad a nivel de lectura, ya que de ello se encarga el subsistema de consultas, que puede tener otro mecanismo de persistencia totalmente distinto. Además nos basta con un mecanismo que permita escribir en modo «append only» y soporte consultas cronológicas y por tipo. ¿Cómo implementamos pues la persistencia?

Alternativas de implementación

Podemos, como siempre, usar una BBDD relacional como soporte sobre el que almacenar la secuencia de eventos. Pero aunque factible, no es muy ventajoso. Al fin y al cabo nunca vamos a consultar por columnas, o por clave primaria. Tampoco vamos a tener que hacer joins. Ojo, no digo que no podamos usar SQL, lo que quiero hacer notar es que en este escenario el paradigma relacional no nos ofrece muchas ventajas.

Parece mejor usar un paradigma más sencillo. Por ejemplo, en un clave/valor podríamos usar como clave el número de orden del evento y como valor el propio evento serializado directamente. Una simple consulta por rango de clave nos valdría. Otra opción es usar algo como REDIS, que tiene soporte directo para listas y operaciones atómicas de incremento. Por otro lado, algunos sistemas noSQL, como Cassandra optimizan la escritura sobre la lectura, lo que viene bien en el caso de event sourcing.

Podemos simplificar aun más, y usar directamente el sistema de ficheros. Si lo pensáis bien en Event Sourcing no hay ni modificación ni borrado. Toda modificación en el sistema ocurre a través de un comando, que como resultado cambia el estado de este. Como consecuencia del cambio de estado siempre se produce un nuevo evento, que habremos de añadir a la secuencia de eventos ya existente. Lo mismo ocurre con las operaciones de borrado. A nivel de negocio raramente ocurre un borrado como tal. Podemos cancelar un pedido, o anularlo, o sea ejecutar otro comando que termina produciendo otro evento. Como se ve no se necesita ni modificar ni borrar ningún evento. Esto es importante porque tanto los discos magnéticos como los SSD se comportan bastante bien con sistemas que sólo añaden información por el final. En teoría no debería ser muy complicado hacer un sistema que abra un fichero en modo append only… qué curioso acabo de resucitar el concepto de log transaccional o log de operaciones, que se ha usado durante décadas en sistemas transaccionales robustos. Pero meterse a niveles tan bajos sólo compensa si realmente necesitas mucho rendimiento.

Ya existen sistema de persistencia especializados para event sourcing. Son los llamados Event Storages. Sin embargo todos los que conozco no acceden directamente al sistema de ficheros, sino que mapean el concepto de event sourcing sobre otros motores de persistencia como mySQL, Redis o Cassandra.

Snapshoting

Sin embargo la técnica de event sourcing tiene dos debilidades: ficheros que siempre crecen y arranques de sistema lentos.

Como vimos, no es común hacer borrados de los eventos ya que casi todas las operaciones terminan en un evento nuevo que hay que añadir al sistema de ficheros. Esto hace que el espacio de almacenamiento requerido crezca sin parar. Podemos definir eventos de negocio que ocupen muy poco espacio, y usar técnicas de compresión, pero eso sólo hace que el espacio ocupado crezca más lentamente.

Por otro lado, cada vez que el sistema se para y tiene que ser reiniciado, debe leer todo el log de eventos y reprocesarlos para obtener una instancia del modelo de negocio en memoria. Esto hace que el tiempo de arranque sea largo. Este problema afecta al subsistema de comandos principalmente. El subsistema de consultas suele almacenar directamente el estado del modelo de consultas, y sólo necesita procesar los eventos desde la última vez que se paró. Por lo tanto este problema no es importante en el caso del subsistema de consultas.

En los casos en que estos dos problemas son importantes, no se usa event sourcing puro, sino una técnica híbrida llamada snapshoting. La idea es usar event sourcing, pero en segundo plano, y cada cierto tiempo, ir procesando los eventos para generar una instantánea del estado del sistema que será persistida. Esta instantánea estará desfasada con respecto a la secuencia de comandos. La idea es almacenar tanto el log de eventos como la instantánea, por lo que se puede considerar event sourcing puro. Las ventajas son las siguientes:

  • Evitamos volver a procesar el log de eventos completo en caso de arranque. Cuando el sistema arranca lee la instantánea, y procesa sólo los eventos que ocurrieron posteriormente a la creación de dicha instantánea.
  • Se pueden borrar los eventos anteriores a la creación de la instantánea, a condición de que se pueda generar una secuencia de eventos equivalente a ella a partir de la instantánea.

Conclusión

Aprovechando que estamos usando eventos de negocio para sincronizar ambos subsistemas, y usar Event Sourcing como paradigma de persistencia en sistemas CQRS. Este enfoque se integra de forma natural con CQRS, es sencillo de mantener y evolucionar, y en general es una buena base para conseguir sistemas escalables. A nivel de rendimiento es importante que nuestro mecanismo de persistencia sea capaz de escalar bien en escritura.

En el próximo post nos adentraremos en el mundo del subsistema de consultas.


En el anterior post sobre CQRS vimos que existían muchas formas de implementar este estilo arquitectónico, ya que había que tomar varias decisiones de diseño, principalmente que paradigma de persistencia íbamos a usar en el subsistema de comandos y en el de consultas, el método de sincronización entre éstos y el nivel de consistencia que nos interesa. En este post nos centraremos en la sincronización.

¿Qué sincronizamos?

Dejando de lado la tecnología concreta que pudiéramos usar para sincronizar ambos subsistemas, nos surge una pregunta interesante: cuando decimos que ambos subsistemas se sincronizan, ¿cual es exactamente la información que se intercambia entre ambos? ¿Qué es lo que enviamos y recibimos a través de ese mecanismo de sincronización? Si no queremos darle muchas vueltas, podemos simplemente hacer que el nuevo estado, resultante de una operación de negocio, sea recibido por el subsistema de consulta. Es una solución sencilla y directa, pero tiene sus problemas:

  • Transmitir el estado completo una y otra vez puede ser ineficiente.
  • Una de las ventajas de CQRS es precisamente que ambos susbsistemas pueden tener modelos de información diferentes, uno optimizado para operaciones y otro para consultas. Si transmitimos el estado del modelo del subsistema de comandos, la transformación de este en el modelo de información de consulta puede ser costosa e implicar una lógica compleja.

Otro enfoque al problema de la sincronización es transmitir sólo los cambios en el estado del sistema, y no el nuevo estado al completo. De esta forma nos evitamos enviar bastante información, con lo que solucionamos el primer problema. Ummm, dos subsistemas que se sincronizan enviando únicamente los cambios de estado, esto suena sospechosamente a eventos. Técnicamente sólo serían eventos si decidimos que nuestra sincronización siga un paradigma «push» (publish/subscribe). Pero alternativamente podríamos usar un paradigma «pull», donde el subsistema de consulta pregunta al de comandos qué cambios se produjeron desde un determinado momento. En este caso alguno podría argumentar que no son estrictamente «eventos» sino «cambios». Yo, por simplicidad, hablaré siempre de eventos, tanto en el caso de «pull» como de «push».

Ya estoy viendo que a más de uno le ha hecho «click» en la cabeza, y ha visto la luz: «claro, ahora si cambio la propiedad en una entidad, emito un evento «property change», y si creo una instancia emito un evento de «create», con lo cual usando el «framework X» lo tengo resuelto y …» Yo mismo he pensado esto en su momento, pero claramente este enfoque nos lleva al lado oscuro del CRUD, que siempre está ahí para tentarnos. A este enfoque lo llamo usar eventos CRUD, y no lo recomiendo en general para CQRS. El problema es que el subsistema de consulta debe ser capaz de interpretar los eventos de la forma más desacoplada posible del subsistema de comandos, pero los eventos CRUD están definidos en función del esquema de datos del subsistema del comandos. Si al subsistema de consultas le llega un evento «property change de la propiedad P sobre la entidad E», éste debe conocer el esquema de datos del subsistema de comandos para poder interpretarlo. Si hiciéramos un cambio en dicho esquema, tendríamos seguramente que modificar el código en el subsistema de consulta, aunque no se haya producido ninguna modificación en la lógica de negocio. Es más, es probable que haya eventos que ya no tengan sentido, al desaparecer alguna entidad o propiedad. Lo que se intenta con CQRS es que ambos subsistemas puedan evolucionar por separado, y para ello necesitamos que el acoplamiento entre ambos sea bajo. Por lo tanto evitad los eventos CRUD, excepto en el caso que tengáis una necesidad real del mismo esquema de datos en ambos subsistemas (con lo que CQRS no sería una solución tan ventajosa).

Lo que necesitamos son eventos de negocio, que representen cambios en el estado del modelo de negocio, no en el esquema de datos. Para averiguar que eventos de negocio tenemos, debemos hacer antes un buen análisis de la funcionalidad de negocio. Esto es consistente con CQRS, ya que de todas maneras debemos averiguar que operaciones de negocio (comandos) tenemos, por que estados pasa el sistema, etc. Por ejemplo, en una tienda electrónica debemos modelar el ciclo de vida de un pedido. Podemos llegar a algo como esto:

A simplified order lifecycle

A simplified order lifecycle

Existiría un comando «Checkout» que una vez ejecutado por el subsistema de comandos, podría hacer evolucionar el estado del pedido desde «Open» a «Accepted«, o en el caso de un problema, tal vez de pago, al estado «Rejected«. Estas transiciones de estado generarían respectivamente los eventos de negocio «OrderAccepted» y «OrderError«. Ambos eventos transportarían el identificador de pedido y quizás algún campo auxiliar, como por ejemplo, una razón para el rechazo del pedido. Obviamente hay bastantes comandos, estados y eventos, y seguramente falten cosas, pero el hacer un pedido implica un proceso de negocio no trivial.

De esta forma, el acoplamiento entre ambos subsistemas se basa sólo en el formato de los eventos, y en compartir un modelo de negocio común, pero el subsistema de consultas no tiene porque saber como se han estructurado las tablas o los objetos en el subsistema de comandos. El ciclo de vida de un pedido va a cambiar sólo cuando cambie el negocio, no por cualquier detalle técnico, por lo tanto constituye un contrato más estable que un esquema de datos.

El lector avezado habrá notado que esto está muy alineado con el enfoque Domain Driven Design (DDD). Operaciones de negocio, eventos de negocio, estados por los que evoluciona un pedido, etc. Todo esto está muy relacionado con el concepto de lenguaje ubicuo de DDD, no es de extrañar que CQRS sea una arquitectura a la que se suele llegar aplicando la metodología DDD, y viceversa, forzándote a hacer CQRS es fácil que termines haciendo DDD.

Conclusión: una forma muy apropiada de sincronizar ambos subsistemas es mediante eventos de negocio.

¿Cómo sincronizamos?

Desde el punto de vista tecnológico, lo primero a decidir es si queremos un mecanismo de notificación «pull» o «push».

El paradigma «pull» se basa en que el cliente pregunta al servidor para obtener los datos. Como estamos hablando de pedir cambios, el cliente debe hacer «polling», es decir repetir la consulta cada cierto tiempo. Esto en algunos casos es ineficiente, ya que muchas veces el cliente va a preguntar en vano, ya que no hay cambios que notificar. De esta manera vamos a estar preguntando y consumiendo recursos para no conseguir nada. Sin embargo en algunos casos es ventajosos usar este paradigma:

  • Es muy simple, no se necesita ningún producto ni protocolo sofisticado para llevarlo a cabo.
  • Internet y la web están diseñados para el paradigma «pull». Por lo tanto están optimizados para este tipo de interacción, en concreto el uso correcto de las caches, las peticiones HTTP condicionales y el protocolo ATOM/RSS, nos permiten implementar este enfoque de forma eficiente.
  • Es muy interoperable.
  • El servidor (en este caso el subsistema de comandos) está totalmente desacoplado de los clientes. No necesita saber cuantos clientes tiene, ni que eventos recibió cada cliente, ni nada. Simplemente responder a las consultas. Es decir podemos implementar un servidor stateless, que es más sencillo y escalable.

Por lo tanto en algunos escenarios es bastante interesante usar este enfoque, por ejemplo:

  • El subsistema de consulta y el de comandos se comunican a través de la web. Tal vez ambos están en diferentes máquinas en la nube (ideal para alta disponibilidad). En este caso se puede usar HTTP con ATOM/RSS para distribuir los eventos mediante una interfaz REST.
  • Una tercera parte ha implementado una aplicación de agregación de información que usa nuestros eventos. Tal vez sea un business partner, otra empresa de nuestro grupo, el departamento del edificio de al lado, o quizás la nuestra sea una startup que quiere ofrecer una API. De nuevo lo lógico es publicarlo mediante una API REST, que es inherentemente «pull».

En general todos estos escenarios implican una alta latencia, lo que provoca que las consultas puedan estar segundos o minutos atrasadas con respecto al estado real del sistema.

El paradigma «push», donde el cliente se subscribe al servidor y recibe los eventos sólo cuando ocurren. El cliente no necesita preguntar al servidor periódicamente, es notificado cuando es necesario. La principal desventaja de este sistema es el acoplamiento entre el servidor y el cliente. El servidor debe mantener una lista de subscripción de forma persistente, y almacenar que eventos fueron enviados a que clientes. Esto puede producir un problema de escalabilidad si tenemos un número ilimitado de clientes. También complica el diseño del subsistema de comandos. En general esto se solventa usando mecanismos de mensajería como JMS o AMQP. También se pueden usar productos de middleware, por ejemplo un producto de Enterprise Service Bus.

Algunos escenarios:

  • El subsistema de consulta y el de comandos están en el mismo proceso. Nos basta con implementar un patrón «Observer».
  • Ambos subsistemas están en la misma máquina o en el mismo CPD. Usaríamos un sistema de mensajería o un Enterprise Service Bus

En resumen, existen a nivel tecnológico muchas variantes y soluciones para implementar el mecanismo de sincronización. Lo importante es que transmitamos eventos de negocio mediante este.

En el próximo post nos adentraremos en el mundo del subsistema de comandos.


A la vuelta de las vacaciones me han entrado ganas de escribir algo. Sin ganas de hacer el típico post retrospectivo del SpainJS, y por petición popular, he decidido escribir sobre ese gran desconocido que es CQRS. Como no quiero que vuestros ojos se fatiguen mi plan es hacer varios posts cortos en vez de uno con la longitud acostumbrada. En éste me centraré en hacer una introducción al tema. Ya nos meteremos un poco más en detalle en los siguientes.

En una arquitectura tradicional, tenemos un único sistema que se encarga de realizar operaciones de negocio y nos permite consultar la información en la que se encuentra nuestro sistema. Sin embargo, esto viola el principio de única responsabilidad (SRP), ya que el mismo sistema se encarga de hacer dos cosas que en principio son muy distintas: exponer operaciones que evolucionen el estado del sistema de forma consistente, y leer el estado del sistema. Estas dos responsabilidades tienen requisitos muy diferentes desde distintos puntos de vista: funcional, escalabilidad, tiempo de respuesta, seguridad, criticidad, etc. Si nos tomamos en serio el principio de separación de responsabilidades, y somos partidarios de arquitecturas modulares en vez de sistemas monolíticos y centralizados, debemos buscar  una arquitectura alternativa.

Command Query Responsability Segregation (CQRS), es un estilo arquitectónico en el que tenemos dos subsistemas diferenciados, uno responsable de los comandos, y otro responsable de las consultas. Por comando entendemos un petición por parte del usuario u otro sistema, para realizar una operación de negocio, que evolucione el sistema de un estado a otro. Cada uno de estos subsistemas tiene un diseño, modelo de información y mecanismo de persistencia diferente, optimizado para las tareas que deba afrontar. Normalmente el subsistema de consulta suele ser mucho más simple que el otro. Veamos la siguiente figura para explicar un poco como funciona esto (se nota que soy un artista):

CQRS Architecture Overview

CQRS Architecture Overview

El subsistema de comandos, simplemente recibe peticiones de comandos, valida que éstos son consistentes con el estado actual del sistema, y si es así los ejecuta. Como resultado de la ejecución de un comando, el estado del sistema cambia, y ese cambio se comunica al subsistema de consultas mediante algún mecanismo de sincronización.

El subsistema de consulta recibe los cambios en el estado del sistema mediante el mecanismo de sincronización. Durante la etapa de filtrado ignora los cambios en los que no está interesado. En algunos mecanismo de sincronización esta etapa puede formar parte de la configuración del mismo, y no del código de aplicación. Después los cambios se pueden pasar por una etapa opcional donde se pueden transformar, añadirle información calculada y agregar información varios cambios. De nuevo en algunos casos el mismo mecanismo de sincronización podría proporcionarnos herramientas para definir esta etapa de forma declarativa. Finalmente se actualiza de forma adecuada la base de datos. La ejecución de la consulta simplemente consiste en exportar los datos de la BBDD como DTOs y serializarlos en un formato adecuado.

Si en el subsistema de consulta usamos una BBDD relacional, podríamos definir un esquema optimizado para las consultas, de forma que estas no necesiten joins, y las sentencias SELECT sean muy simples y sencillas de optimizar. Tal vez una tabla por consulta, con una columna por parámetro de consulta, y una columna adicional con el resultado en formato ya directamente serializado. Evidentemente otras opciones son posibles.

Y ya está, cualquier sistema que cumpla lo descrito se puede considerar CQRS. Ahora bien, como el demonio está en los detalles, existen muchos puntos importantes a decidir:

  • La naturaleza exacta del mecanismo de sincronización entre el subsistema de comandos y el de consultas. Se puede usar algo tan simple como una llamada local intraproceso, o algo más sofisticado como un sistema de colas, o un servicio web (REST o SOAP). También hay que decidir si la sincronización será pull o push, es decir, ¿el subsistema de comandos notificará al de consulta los cambios? ¿O tal vez el subsistema de consultas preguntará de forma periódica que cambios se produjeron desde la última vez?
  • El paradigma de persistencia de cada subsistema. Puede ser SQL tradicional, o noSQL. Lo interesante es que cada subsistema puede usar el paradigma que más le interese. En concreto si usamos relacional para las consultas, se nos plantea la disyuntiva de usar un esquema desnormalizado totalmente optimizado para las consultas (una tabla por consulta con las columnas específicas a recuperar por ésta) o bien un esquema en tercera forma normal que nos de más flexibilidad.
  • Si el subsistema de comandos tiene persistencia o no. En realidad el subsistema de comandos sólo necesita consultar el estado para saber si puede ejecutar un comando o no. Esta consulta podría proceder del subsistema de consulta, en vez de una BBDD propia. Esto puede ser una opción según que modelo de consistencia sea admisible.
  • Transaccionalidad en el subsistema de comandos. ¿Cuándo se da por terminada la transacción representada por el comando? ¿Cuándo el nuevo estado del sistema se ha modificado en memoria? ¿O tal vez, cuando este estado se ha persistido en la BBDD específica del subsistema de comandos? ¿Quizás sería mejor esperar a que el subsistema de consulta haya recibido el cambio y actualizado el modelo de consulta? Esta decisión es crítica a la hora de definir el modelo de consistencia de datos que queremos (ACID o BASE).
  • ¿Un sólo subsistema de consulta o varios? La arquitectura CQRS nos permite definir varios subsistemas de consultas, especializados en cosas diferentes. Cada uno puede tener un enfoque diferente en cuanto a la persistencia y consistencia. Por ejemplo uno podría usar un sistema noSQL para consultas específicas, y otro podría ser un sistema OLAP para hacer minería de datos.
  • ¿Un sólo subsistema de comandos o varios? De nuevo CQRS nos da la libertad de tener distintos subsistemas especializados en distintas familias de comandos. Uno para procesar comandos relacionados con los pedidos, otros para el control del stock, otro para pagos.

Muchas decisiones, muchas combinaciones, cada una de ellas con su correspondientes ventajas e inconvenientes. Los siguientes posts de la serie tratarán de explorar un poco estos aspectos.

El usar CQRS nos da los siguientes beneficios:

  • Ambos subsistemas pueden evolucionar por separado; el mantenimiento y despliegue puede estar diferenciado. Esto es una ventaja porque normalmente el ritmo de cambios en la funcionalidad es muy diferente en las consultas y los comandos. En mi experiencia es más frecuente añadir una nueva consulta que un nuevo comando.
  • Ambos sistemas pueden escalar por separado. Si nuestro negocio es muy intensivo en lecturas, podemos dedicar más máquinas al subsistema de consultas de forma sencilla. Simplemente levanta otra instancia y conéctala de forma que pueda recibir los cambios transmitidos por el subsistema de comandos.
  • El estilo CQRS es modular de forma inherente. Podemos escoger ir a una arquitectura modular, donde haya varios subsistemas de consulta y varios subsistemas de comandos. Cada uno de ellos de nuevo puede ser diseñado, mantenido, escalado y desplegado por separado. Es muy interesante la posibilidad de añadir nueva funcionalidad (nuevos subsistemas de comandos o consultas) sin tener que parar el resto del sistema.

Sin embargo todo esto no es gratis:

  • Es más complejo, sobre todo debido a que debemos diseñar con cuidado el mecanismo de sincronización en los comandos y las consultas.
  • El tener sistemas de persistencia separados para los comandos y las consultas, o el tener múltiples subsistemas de cada uno, puede provocar que tengamos cierta duplicación de información. Esto puede ser negativos si estamos preocupados por ahorrar espacio en disco.
  • Para construir el subsistema de comandos necesitamos saber que comandos existen, como evolucionan el estado del sistema y cuando la ejecución de un comando es consistente con el estado del sistema. Todo esto exige modelar bien el negocio, olvidarse de las tablas y centrarse bien en las acciones del usuario y modelar bien los casos de uso como máquinas de estado o similar. O sea, no sirve para hacer aplicaciones de mantenimientos de tablas o CRUD. El problema es que muchos frameworks de moda (ROO, GRAILS, RAILS…) están optimizados para ser productivos haciendo CRUD, no para CQRS. No digo que sea imposible usar, por ejemplo RAILS, pero sí es verdad que no va a ser tan productivo.

Los dos últimos puntos son dificultades relativas. El tener duplicación de información y distintos subsistemas es bueno desde el punto de vista de la redundancia y la disponibilidad de la información. Si un subsistema de consultas se corrompe, se puede reconstruir a partir del subsistema de comandos. Por otro lado el no poder usar un enfoque CRUD, o que los frameworks de moda no nos faciliten tanto la tarea, lo veo más un problema social que de ingeniería.

¡ Anda, creo que me ha quedado un post corto !


Este post no es para tratar de convenceros de que os unais a las legiones de Javascript, sino para explicar mis razones para tomar ese camino de perdición. Antes que en javascript programé en tres o cuatro lenguajes, principalmente C y Java. Por supuesto en la universidad aprendí LISP, PROLOG, ADA, varios dialectos de ensamblador y alguna cosa más, pero como nos lo he vuelto a tocar desde entonces no los considero parte de mi «toolkit».

Yo empecé en esto de Javascript como la mayoría de vosotros, haciendo unos scripts güarrotes para validaciones y efectillos, en los tiempos del año 2000. Era un mal necesario con el que tenía que convivir al desarrollar mis preciosas aplicaciones thin-client en Java. Sin embargo sobre 2005, empecé a querer usar servicios REST sin sesión por temas de rendimiento, lo que me hizo repensar la arquitectura de mis sistemas.  Fue entonces cuando descubrí AJAX, y me quede fascinado por las posibilidades. Desde entonces fue un camino sin retorno; cada vez que aprendía más, descubría nuevas tecnologías, y reflexionaba más sobre como diseñar mejor mis aplicaciones, más enganchado me quedaba con Javascript, y desde entonces hasta ahora.

Así que, ¿por qué Javascript?

Es ubíquo

Que yo sepa, es el lenguaje que en la actualidad está más extendido del mundo. Virtualmente en cada ordenador de cada casa se puede ejecutar una versión u otra de Javascript. ¿Qué ordenador no tiene un browser? ¿Al menos un IE6? Sí, algunas personas por motivos de accesibilidad o seguridad no lo tienen disponible, pero son una exigua minoría.

¿Y en servidor? Pues ya sabeis que tenéis el node.js, tenéis Rhino, etc. En concreto en node.js se está invirtiendo cantidades ingentes de dinero. Ya está soportado en algunas plataformas cloud, como Heroku/Cedar y Windows Azure.

¿Y en desktop? Pues desde hace tiempo existe la plataforma Mozilla, ¿en que creéis que se programa la mayor parte de Firefox y Thunderbird? En una mezcla de Javascript (y C para el bajo nivel). Además Windows 8 plantea usar a JavaScript como uno de los lenguajes de preferencia para hacer aplicaciones de escritorio (otorgándole el mismo rango que a C#).

El mismo lenguaje para cliente y servidor

A día de hoy Javascript es el único lenguaje en el que puedes desarrollar por completo una aplicación web de punta a punta, desde la UI hasta el acceso a BBDD. ¿Qué tiene esto de interesante?

  • No necesitas dominar más de un lenguaje para hacer la aplicación web. Esto te permite no estar cambiando de contexto entre un lenguaje y otro, o simplemente no necesitar dos equipos de desarrollo especializado (uno de front-end y otro de server-side). Además de aumentar tu productividad, puede facilitar tener un equipo de proyecto más cohesionado.
  • No necesitas recodificar la misma lógica tanto en Javascript como en el lenguaje que estés usando en tu server-side. ¿Modelos de negocio? ¿Validaciones? Todo eso es único y debería ser reutilizable tanto en tu front-end como en tu server-side. Si lo haces todo en Javascript, terminas teniendo un montón de código común en ambos lados, que antes tendrías que haber duplicado y mantenido en dos lenguajes diferentes. Eso sí, hay que diseñar tu arquitectura de la forma adecuada para que esto se pueda cumplir.
  • Estándares de codificación y criterios de calidad unificados para todas las capas de tu sistema.
¿No veis todo esto como una «killer feature» de Javascript? Yo personalmente el día que descubrí node.js, vi el cielo abierto, y me dije «esto es la caña».

Tipado dinámico

Confieso que este punto es muy subjetivo. Ya sabeis que soy un fanático del TDD. Y si haces TDD como es debido, al final el compilador deja de ser tan valioso. Los errores de sintaxis o de tipos son muy sencillos de detectar y arreglar usando TDD, no necesitas un compilador para ello. En los lenguajes de tipado estático, el compilador detecta este tipo de errores, pero a cambio tienes que esperar a que éste se termine de ejecutar. Además normalmente tendrás que escribir más código, para añadir toda la información de tipado que el compilador necesita. Esto hace que el ciclo de TDD sea algo más lento que en un lenguaje de tipado dinámico. Ya que TDD me va a detectar esos errores de todas formas, no me resulta rentable el «trade-off» propuesto por el compilador.

Dicho esto, si no hacéis TDD, mucho cuidado con Javascript, puede ser un buen dolor de muelas de depurar, e incluso pequeños fallos de sintaxis te pueden llevar por el camino de la amargura. Para mi lo ideal del TDD es no tener que depurar, o si lo tengo que hacer, que sea sobre 4 o 5 líneas de código bien identificadas, y que están siendo ejercitadas por un test predecible. Desde este punto de vista Javascript no plantea ningún problema.

Paradigma mixto

Es un lenguaje que te permite hacer OO hasta de dos formas diferentes, con «extends» y sin «extends». Pero es que además también puedes hacer programación funcional con él. Esto te permite elegir el paradigma de programación que mejor se adapte al problema a resolver.

¿Tratamiento de colecciones? ¿Cálculos puros (validaciones, reglas de negocio)? ¿Programación asíncrona? Mejor el paradigma funcional

¿Modelado de negocio? ¿DDD? Mejor OO

Y por supuesto en una aplicación de verdad vas a tener que usar ambos paradigmas, y mezclarlos armoniosamente. Incluso a veces los puedes mezclar de tal forma que llegan a ser difícil de distinguir uno de otro.

Flexible

Es un lenguaje ideal para hacer DSLs internos, gracias a su gran flexibilidad. Puedes añadir y eliminar funciones al vuelo tanto en una instancia como un constructor. Esto no sólo te permite crear DSLs sino añadir nuevas capacidades al lenguaje, y realizar metaprogramación (aunque en Ruby y Groovy se puede llegar incluso más lejos).

Reconozco que esto puede ser una fuente de problemas, y es mejor evitar este tipo de funcionalidades. Por ello las últimas versiones de Javascript te permiten cerrar total o parcialmente un objeto, para evitar que se puede modificar en tiempo de ejecución. Sin embargo en algunos casos está justificado hacer este tipo de cosas. Aparte de los DSLs y quizás algunos ejemplos de metaprogración, el caso más importante es el de retrocompatibilidad. ¿Qué ocurre si estoy ejecutándome en una máquina virtual de Javascript que no soporta la API de colecciones de la última versión de Javascript? ¿Y si estoy en un navegador que no soporta alguna API de HTML5? ¡No hay problema! Con Javascript puedo detectar tal eventualidad y añadir la API que me falta de forma dinámica. Este tipo de capacidad ha permitido a Javascript sobrevivir en el infierno de incompatibilidades de los distintos browser por más de 10 años.

¿Y los WTF?

Sí, Javascript tiene algunos WTF. Sólo es cuestión de comprenderlos y saber que están ahí. Por ejemplo, los ámbitos de visibilidad no se señalan con ‘{‘ y ‘}’. En Javascript las llaves son prácticamente cosméticas y sólo sirven para agrupar el cuerpo de bucles y condicionales. Otra tema es el hoisting de variables, o el hecho de que en Javascript no existan los números enteros sino que sean todos en coma flotante. Personalmente el WTF que más me molesta es que Javascript tiende a ser «listillo». Esto significa que intenta no fallar nunca con excepción. De ahí el horroroso e infernal sistema de coerción de tipos o el abuso de null y NaN en mucha de las librerías del lenguaje. En esto sí es verdad que se sufre.

Sin embargo los WTF de Javascript no son tantos ni tan difíciles de aprender como la gente piensa. La mala fama de Javascript viene por causas totalmente ajenas al lenguaje, los WTF de los navegadores. Tradicionalmente la implementación de los navegadores de los estándares de manipulación de HTML y de los servicios estandarizados por W3C ha sido defectuosa. Eso ha causado multitud de quebraderos de cabeza a los desarrolladores. Realmente, en la mayor parte de los casos, cuando un desarrollador piensa en las heridas recibidas en acto de servicio por parte de Javascript, normalmente debería achacarle la culpa al navegador. Desgraciadamente hasta hace poco navegador y lenguaje han estado mezclados en las mentes de los desarrolladores.

El pero

No todo es bonito en el lenguaje. Hay varios problemas con Javascript. El primero, y más importante, son los WTF arriba mencionados. El segundo es la sintaxis tan fea que tiene. La verdad de que con esto de querer simular ser C o Java la pifiaron bien. Espero que en siguientes versiones del lenguaje arreglen todo esto. Si no de momento podemos usar «strict mode»; para desactivar la mayor parte de los WTF, o usar CoffeeScript. El caso de CoffeeScript es curioso, si sabes Javascript entonces CoffeeScript te parece un simple dialecto de Javascript. Si no conoces Javascript, te parecerá un lenguaje totalmente diferente. El caso es que CoffeeScript soluciona la mayor parte de los WTF de Javascript, y tiene una sintaxis mucho más elegante. Lo tengo en el punto de mira a ver como se desarrolla.

Estos son los problemas que reconozco con el lenguaje. Después hay problemas humanos. Ese programador de Java que intenta programar en Javascript como si fuera un hermano retardado de Java (me incluyo en los culpables). Claro, intentas que una manzana se parezca a un plátano, y después vas echando pestes de lo feas que son las manzanas. No critiquemos el lenguaje sólo porque tengamos un conocimiento superficial de este, y al intentar programar como lo hacemos en nuestro lenguaje server-side, las cosas no nos salgan bien. ¡Es como si yo ahora digo que Clojure es una mierda porque no me termino de acostumbrar a su sintaxis y no puedo hacer una asignación de variable de forma sencilla! (no sería mal post 😀 )

También hay que reconocer que no es un lenguaje sencillo de aprender. Primero tienes que aprender los WTF, para desarrollar instintivamente buenas prácticas que los eviten. Después tienes que ser capaz de dominar dos paradigmas de programación distintos, OO y funcional. Sólo así conseguirás sacarle toda la potencia a Javascript.

Conclusión

Desde mi punto de vista a día de hoy Javascript es el lenguaje más adecuado para desarrollar aplicaciones web. El problema es que no tiene competencia, ¿qué otro lenguaje tiene unas características similares a las anteriormente mencionadas? Yo no conozco ninguno. Ciertamente Ruby es bastante elegante, y me gusta bastante, ¡ pero sólo se ejecuta en servidor ! Java no está envejeciendo nada bien, y aunque C# es más avanzado está mucho menos extendido y tampoco se ejecute en servidor. Lo mismo puede decirse de Clojure, Scala o Groovy. Sí, podemos hacer como GWT o ClojureScript y compilar estos lenguajes en Javascript, pero, ¿con qué fin? La experiencia GWT ha resultado fallida.

En cualquier caso, a día de hoy, programes en el lenguaje que programes, si quieres hacer aplicaciones web modernas, te toparás con Javascript. Así que es mejor aprenderlo y dominarlo.

Tal vez en el futuro salga un nuevo lenguaje, con más ventajas que Javascript para hacer aplicaciones web. En ese momento tiraré Javascript a la basura y me pondré con el nuevo lenguaje, pero me temo que aun quedan 10 años para eso.

¡Espero que os hayan sentado bien las torrijas de Semana Santa!


Este es un pequeño post que tiene doble objetivo, por un lado quiero contar un poco como fue mi día de codemotion y las charlas a las que fui, por otro lado quiero aclarar algunos aspectos de la charla que di. Empecemos por lo primero.

Las charlas a las que yo asistí

La primera charla que vi fue sobre crear aplicaciones nativas en Widows 8 con Javascript, de Boris Armenta (@borisarm). La verdad es que la charla estuvo interesante, y Boris habló justo sobre lo que decía el título de su charla. Empezó con una pequeña introducción sobre la nueva plataforma de desarrollo de Windows 8, y después hizo una demo. La verdad es que tenía mucha curiosidad por ver como lo habían montado los señores de Microsoft para poder desarrollar aplicaciones nativas con JavaScript. Por lo que Boris contó no se lo han montado mal. Por un lado han orientado las APIs de desarrollo al paradigma de aplicaciones táctiles y no de ratón. En el caso de que el dispositivo no sea táctil, Windows 8 adaptará nuestra aplicación al ratón y teclado de forma transparente al programador. Por otro lado han cogido la máquina virtual de JavaScript de IE9, llamada «Chakra», y la han extraído del browser para que podamos ejecutar aplicaciones javascript de escritorio en modo nativo, totalmente separado del browser. Ahora con JavaScript puedes acceder a la API del sistema operativo de forma directa y no estás restringido por el sandbox del browser. ¿Y como pintas las UI? ¡Pués con HTML5 y CSS! Windows 8 te proporciona una sintaxis de microdata para marcar el HTML y hacer bindings de este con tu librería de widgets en javascript. Eso sí, Boris se cuidó mucho de decir que esos bindings sólo funcionan para aplicaciones nativas, que nos olvidáramos de usarlos en IE. Sabia decisión para evitar problemas con los estándares. Parece que Microsoft está aprendiendo. Una apreciación personal (Boris nunca dijo nada parecido), me da la impresión que el tema silverlight y XAML ha quedado en segundo plano, como si Microsoft se estuviera preparando para marcarlo como obsoleto. Inquietante y esperanzador a la vez.

Curiosamente, por la tarde me metí en otra charla de .NET, se trataba de las extensiones reactivas de .NET, presentado por Fernando Escolar (@fernandoescolar) y Quique Martínez (@quiqu3). La verdad es que se lo montaron bien, con una dinámica de presentador «gerente» vs. «técnico» divertida. El tema fue las nueva extensión de .NET para programación reactiva, que no es más que un DSL para manejar relaciones observador/observer y bindings de forma  sencilla y elegante, mediante una fluent API. Me resultó también curioso que la misma extensión estuviera portada a Javascript. ¿Será Javascript el próximo lenguaje de referencia para .NET? No lo se, pero apunta maneras. En cualquier caso técnicamente no me pareció nada nuevo, ya que se parece mucho a las cosas que se pueden hacer con ember.js o con knockout.js.

Antes del café de la mañana me fui a ver a Arun Gupta hablar sobre el futuro de JEE 7. Me salió la vena nostálgica de «javero empresarial» que tengo, y se me antojó ver si la JEE evolucionaba por buen camino. Me alegré ver que están pensando orientar JEE al mundo del cloud. No sólo eso sino que percibí un esfuerzo serio por facilitar la vida tanto al desarrollador como al personal de sistemas. Creo que están intentando reaccionar para eliminar esa fama que tiene JEE como plataforma excesivamente compleja. También me gustó la idea de integrar OSGi dentro de JEE mediante el proyecto Jigsaw.

Después de la comida me divertí mucho con la charla de Berto Yáñez (@bertez) sobre Popcorn.js, un framework de la comunidad Mozilla para mejorar y ampliar la interacción de Javascript con las etiquetas <video> y <audio> de HTML5. Ambiente distendido, risas y código en directo. Sin duda de lo mejor del día.

Finalmente, como no, saqué el machete y conseguí abrirme paso para conseguir un asiento en la charla de David Bonilla (@david_bonilla). Hizo publicidad de Atlassian de forma muy elegante y para nada encubierta, e hizo alguna que otra crítica constructiva a varias empresas y a algunas actitudes que tenemos los desarrolladores. Por supuesto el tema era interesante de por si, y algo que a mi personalmente me interesa bastante: como atraer a los desarrolladores a tu plataforma/producto para crear un ecosistemas de desarrollo. Conseguir esto es el santo grial, ya que te proporciona una base de desarrolladores enorme que va a extender y enriquecer tu producto de forma totalmente gratuita. En contrapartida los desarrolladores tienen la oportunidad de ganar dinero y/o hacerse una reputación o simplemente divertirse. Quizás el único fallo de la charla de David fue su excesiva duración, ¡ que ya era última hora y estábamos todo deseando ir a unas cañas 🙂 !

Me gustó

Un acierto desde mi punto de vista el reunir a comunidades de distintas plataformas de desarrollo, PHP, Ruby, Python, JavaScript, Java y .NET, y también alguna charla de DevOps y de Agile. La variedad es lo que ha distinguido a esta conferencia de otras. ¡Espero que se rompan las barreras endogámicas que separan a cada comunidad de desarrollo!

Por supuesto me gustó bastante el ambiente, el ver estudiantes siempre es bueno desde mi punto de vista. Es importante que las nuevas generaciones vayan viendo lo que se cuece en el mundo real, ya que desgraciadamente la carrera universitaria suele estar muy alejada de la realidad y anticuada en la mayor parte de los casos (con honrosas excepciones). También es interesante que vean que existen conferencias sobre informática en España, y que no tienen porque irse al extranjero para asistir a una conferencia interesante.

También me pareció un buen detalle el cocktail que los organizadores ofrecieron a los ponentes.

Y como no, lo mejor, que como en todas las conferencias, fue la oportunidad de hacer networking con gente nueva por un lado, y reencontrarse con viejos compañeros de viaje.

Cosas a mejorar

Tal vez el próximo año sería mejor que todos los ponentes comieran junto a los asistentes. Con el cocktail es más que suficiente.

Otro detalle fue que hubo demasiada gente. Esto hizo que todas las salas estuvieran muy llenas y que en algunos casos se estuviera bastante incómodo.

UPDATE: Se me olvidaba, hubiera sido muy interesante tener «stands» para las distintas comunidades de desarrolladores que han participado en el evento. Los asistentes podrían haber trabado mejor contacto con otras comunidades de esta manera. Como co-organizador de MadridJS, creo que para el año que viene sería muy interesante tener esto.

Sobre mi charla

Tengo que comentar un par de cosas sobre mi charla. Tal vez algunos asistentes se pensaron que iba a hablar más de CSS orientado a objetos. En realidad esta charla es la hermana de otra que di en la XPWeek el año pasado. Para mi las dos charlas constituyen dos caras de la misma moneda, en una me centro más en CSS y en otra exploro más el diseño javascript. Lamento la posible confusión que pueda haber causado el título y la descripción de mi charla (que es responsabilidad mía no de la organización).

En mi charla sobre CSS orientado a objetos que di el año pasado me centré sobre todo en la parte de CSS, y solo hablé de pasada sobre la arquitectura javascript, y está más orientada a diseñadores. Para los realmente interesados, y la gente que se quedó con ganas de saber más de OO CSS, pueden encontrarla aquí.

Sin embargo en la charla que di en codemotion me quise centrar en los valores «ágiles» y en la arquitectura de software para aplicaciones javascript, como medio para facilitar la colaboración eficaz entre desarrolladores y diseñadores. Obviamente está más orientada a desarrolladores. Esta última está colgada aquí. La he hecho con impress.js así que necesitaréis un navegador «moderno» que soporte HTML5 para verla. Por cierto, mi idea era ver bastante código, pero no me fijé en que las charlas eran de 45min y la cosa quedó bastante corta en ese aspecto. Afortunadamente todo el código está disponible para que lo miréis en Github aquí.

En cualquier caso creo que ambas charlas pueden ser interesantes para ambos roles, y quizás ayuden a entender un poco más lo que implica el trabajo de uno y otro.


(Aviso a navegantes: ¡Este post contiene mucho código escrito a altas horas de la noche!)

Hola de nuevo, en este post, el último sobre «extends», voy a intentar mostrar alternativas a la herencia de implementación. Se trata de la técnica genérica llamada «composición y delegación», técnica en la que se basan varios patrones de diseño, como el famoso «Command», el «Strategy», o el «Decorator» entre otros. Antes de nada voy a resumir lo contado hasta ahora. En el primer post vimos cómo usar la herencia de implementación de forma «ingenua» nos podía meter en un buen lío, ya que sin darnos cuenta podemos romper la encapsulación. En el segundo post intentamos arreglar los problemas usando la palabra reservada «final» y el patrón «Template Method». Si bien al principio parecía que funcionaba, más tarde al ir añadiendo funcionalidades, nos dimos cuenta de que violábamos el principio DRY y se producía una explosión combinatoria de clases, además de dificultarnos el testing y tener que planear de antemano los «ejes» de extensibilidad de nuestra clase.

El problema más importante es la explosión combinatoria de clases y la violación del DRY. La base de este problema es que la lógica de filtrado, y de postprocesamiento de la inserción de elementos, no se encuentran debidamente encapsuladas y abstraídas, sino que están incrustadas dentro de las clases hijas de «ListaSencilla».

Empezando por la lógica de filtrado, ¿no sería mejor si pudiéramos tener una clase donde realmente pudiéramos poner dicha lógica? De esa forma podríamos eliminar ésta de las clases «ListaAuditableConElementosSinEspacios», «ListaAuditableConElementosLongitudPar», «ListaConElementosSinEspacios» y «ListaConElementosLongitudPar». Está claro que estas clases violan el principio de única responsabilidad, ya que implementan el contrato de «Lista» y además tienen lógica de filtrado. Si sacamos esa lógica de filtrado aparte, vamos a quitar responsabilidad de dichas clases y a simplificarlas.

Lo primero de todo sería definir una interfaz «EstrategiaDeFiltrado» que represente el contrato entre una implementación de «Lista» y la lógica de filtrado.

public interface EstrategiaDeFiltrado {
  boolean esInsertable(String elemento);
}

Sencillo, ¿verdad? Ahora sólo tengo que tener una implementación por estragia de filtrado en mi sistema. Primero para los elementos pares:

public final class FiltrarElementosLongitudImpar implements EstrategiaDeFiltrado {
  public FiltrarElementosLongitudImpar() {
    super();
  }
  @Override
  public boolean esInsertable(String elemento) {
    return elemento.length() % 2 == 0;
  }
}

Y ahora para los elementos con espacios:

public final class FiltrarElementosConEspacios implements EstrategiaDeFiltrado {
  public FiltrarElementosConEspacios() {
    super();
  }
  @Override
  public boolean esInsertable(String elemento) {
    return !elemento.contains(" ");
  }
}

Nótese ese «final» para cada clase. Ahora nadie podrá usar herencia de implementación de dichas clases y provocar el caos. Es también interesante notar que con este diseño puedo hacer unos tests muy sencillos de cada implementación de «EstrategiaDeFiltrado». Esto pinta bien, pero aun no hemos arreglado nada. Vamos a ver como reutilizar estas implementaciones de «EstrategiaDeFiltrado» en nuestras listas. Primero un cambio pequeño en «ListaAuditableConElementosLongitudPar»:

public class ListaAuditableConElementosLongitudPar extends ListaAuditable {
  private EstrategiaDeFiltrado filtro = new FiltrarElementosLongitudImpar();
  public ListaAuditableConElementosLongitudPar(Auditor auditor) {
    super(auditor);
  }
  @Override
  protected boolean esInsertable(String elemento) {
    return filtro.esInsertable(elemento);
  }
}

y lo mismo en «ListaAuditableConElementosSinEspacios»:

public class ListaAuditableConElementosSinEspacios extends ListaAuditable {
  private EstrategiaDeFiltrado filtro = new FiltrarElementosConEspacios();
  public ListaAuditableConElementosSinEspacios(Auditor auditor) {
    super(auditor);
  }
  @Override
  protected boolean esInsertable(String elemento) {
    return filtro.esInsertable(elemento);
  }
}

Mmmm, si os fijais «ListaAuditableConElementosLongitudPar» y «ListaAuditableConElementosSinEspacios» tienen exactamente el mismo código, salvo que cada una usa una implementación concreta diferente de «EstragiaDeFiltrado». Una violación del DRY muy clara. Además ese «new» es muy, pero que muy feo ¿No sería mejor pasar el colaborador, es decir, la implementación concreta de «EstrategiaDeFiltrado», por el constructor? Esto nos elimina la duplicación y de paso podemos borrar una clase. ¡ Me encanta cuando puedo borrar código ! ¿Y a vosotros? Ahora ambas clases desaparecen y son sustituidas por «ListaAuditableConFiltro»:

public class ListaAuditableConFiltro extends ListaAuditable {
  private EstrategiaDeFiltrado filtro;
  public ListaAuditableConFiltro(Auditor auditor, EstrategiaDeFiltrado filtro) {
    super(auditor);
    this.filtro = filtro;
  }
  @Override
  protected boolean esInsertable(String elemento) {
    return filtro.esInsertable(elemento);
  }
}

De forma análoga, «ListaConElementosLongitudPar» y «ListaConElementosSinEspacios» acaban siendo eliminadas y sustituidas por «ListaConFiltro». Esto no me termina de convencer. Seguimos violando DRY. Por un lado «ListaConFiltro» y «ListaAuditableConFiltro» tienen lógica claramente duplicada. Por otro lado, el método protegido «esInsertable» es exactamente igual que la interfaz «EstrategiaDeFiltrado». El código pide a gritos ser simplificado, así que, ¿qué tal si nos llevamos todo ese código común a la superclase «ListaSencilla»? El único problema es que de «ListaSencilla» heredan también listas que no tienen filtro, ¿qué hacemos? Podemos aplicar el patrón «null object» y crear una «EstrategiaDeFiltrado» que no haga nada. Veamos ese código:

public final class NuncaFiltrar implements EstrategiaDeFiltrado {
  public NuncaFiltrar() {
    super();
  }
  @Override
  public boolean esInsertable(String elemento) {
    return true;
  }
}

Por otro lado no queremos tener que configurar el colaborador filtro con la clase «NuncaFiltrar» cada vez que queramos una lista sin filtro. Por lo tanto necesitamos que el filtro sea una dependencia opcional dentro de «ListaSencilla». El código quedaría:

public class ListaSencilla implements Lista {
  private List datos = new ArrayList();
  private EstrategiaDeFiltrado filtro = new NuncaFiltrar();
  public ListaSencilla() {
    super();
  }
  @Override
  final public void insertar(String elemento) {
    if (!filtro.esInsertable(elemento))
      throw new ElementoRechazadoError(elemento);
    datos.add(elemento);
    procesamientoAdicionalTrasInsertarElemento(elemento);
  }
  @Override
  final public void insertarVarios(Collection elementos) {
    for (String elemento : elementos) {
      if (!filtro.esInsertable(elemento))
        throw new ElementoRechazadoError(elemento);
    }
    for (String elemento : elementos)
      insertar(elemento);
  }
  public final void configurarFiltro(EstrategiaDeFiltrado nuevoFiltro) {
    this.filtro = nuevoFiltro;
  }
  /**
   * Este método es invocado cada vez que un nuevo elemento
   * es insertado en la Lista
   *
   * @param elemento
   *        El elemento que acaba de ser insertado
   */
  protected void procesamientoAdicionalTrasInsertarElemento(String elemento)
  {
    // Intencionadamente en blanco
  }
  @Override
  public String describirContenido() {
    StringBuilder descripcion = new StringBuilder("Contenidos: ");
    for (String elemento : datos) {
      descripcion.append("'");
      descripcion.append(elemento);
      descripcion.append("' ");
    }
    return descripcion.toString();
  }
}

Obsérvese como se recibe el colaborador mediante un «setter» llamado «configurarFiltro», y no por el constructor. También como se usa por defecto una instancia de «NuncaFiltrar» en caso de que no se configure nada. También hemos eliminado el método protegido «esInsertable».

Finalmente hemos conseguido nuestro objetivo, hemos eliminado todas las subclases de «ListaSencilla» que creamos con el único fin de añadir lógica de filtrado. El patrón que estamos usando es el «Strategy» tal y como apareción en GoF. Podemos volver a usar el patrón «Strategy» para eliminar «ListaAuditable» y el método protegido «procesamientoAdicionalTrasInsertarElemento». Para ello creamos una interfaz «PostProcesador»:

public interface PostProcesador {
  void postProcesar(String elemento);
}

y una implementación «Auditar»:

public class Auditar implements PostProcesador {
  private Auditor auditor;
  public Auditar(Auditor auditor) {
    super();
    this.auditor = auditor;
  }
  @Override
  public void postProcesar(String elemento) {
    auditor.elementoInsertado(elemento);
  }
}

Obsérvese que «Auditar» no es más que un patrón «Adapter», entre la interfaz «PostProcesador» y «Auditor». Además sospechosamente «Auditar» se parece mucho a «ListaAuditable». Finalmente «ListaSencilla» queda:

public final class ListaSencilla implements Lista {
  private List datos = new ArrayList();
  private EstrategiaDeFiltrado filtro = new NuncaFiltrar();
  private PostProcesador postProcesador = new NoProcesar();
  public ListaSencilla() {
    super();
  }
  @Override
  public void insertar(String elemento) {
    if (!filtro.esInsertable(elemento))
      throw new ElementoRechazadoError(elemento);
    datos.add(elemento);
    postProcesador.postProcesar(elemento);
  }
  @Override
  public void insertarVarios(Collection elementos) {
    for (String elemento : elementos) {
      if (!filtro.esInsertable(elemento))
        throw new ElementoRechazadoError(elemento);
    }
    for (String elemento : elementos)
      insertar(elemento);
  }
  public void configurarFiltro(EstrategiaDeFiltrado nuevoFiltro) {
    this.filtro = nuevoFiltro;
  }
  public void configurarPostProcesador(PostProcesador nuevoPostProcesador) {
    this.postProcesador = nuevoPostProcesador;
  }
  @Override
  public String describirContenido() {
    StringBuilder descripcion = new StringBuilder("Contenidos: ");
    for (String elemento : datos) {
      descripcion.append("'");
      descripcion.append(elemento);
      descripcion.append("' ");
    }
    return descripcion.toString();
  }
}

Ahora «ListaSencilla» es «final», sin ningún método protegido y hemos eliminado «ListaAuditable». Hemos conseguido cubrir todos nuestros requisitos sin necesitar la «potencia» de la herencia de implementación. Además hemos simplificado los tests. Es muy sencillo hacer tests de las implementaciones de «EstrategiaDeFiltrado» y «PostProcesador». También los tests de «ListaSencilla» pueden ser simplificados usando dobles de prueba.

Sólo nos queda una cosa, el famoso patrón «Factory»:

public final class NuevaLista {
  public NuevaLista() {
    super();
  }
  private ListaSencilla configurarAuditorEstricto(ListaSencilla lista) {
    lista.configurarPostProcesador(new Auditar(new AuditorEstricto()));
    return lista;
  }
  private ListaSencilla configurarFiltro(ListaSencilla lista,
      EstrategiaDeFiltrado filtro) {
    lista.configurarFiltro(filtro);
    return lista;
  }
  public Lista sencilla() {
    return new ListaSencilla();
  }
  public Lista auditable() {
    return configurarAuditorEstricto(new ListaSencilla());
  }
  public Lista sinElementosConEspacios() {
    return configurarFiltro(new ListaSencilla(),
        new FiltrarElementosConEspacios());
  }
  public Lista sinElementosLongitudImpar() {
    return configurarFiltro(new ListaSencilla(),
        new FiltrarElementosLongitudImpar());
  }
  public Lista auditableSinElementosConEspacios() {
    return configurarAuditorEstricto(configurarFiltro(new ListaSencilla(),
        new FiltrarElementosConEspacios()));
  }
  public Lista auditableSinElementosLongitudImpar() {
    return configurarAuditorEstricto(configurarFiltro(new ListaSencilla(),
        new FiltrarElementosLongitudImpar()));
  }
}

O si son ustedes partidarios de usar frameworks, pueden usar el framework «Spring» que da mucho juego para esto, y programarse exactamente la misma lógica que tenemos en «NuevaLista» pero en XML en vez de en JAVA (es que el XML queda más profesional). Con este patrón «Factory» podemos tener todas las combinaciones de «Lista», «EstrategiaDeFiltrado» y «PostProcesador» que queramos, sin necesidad de volver a tocar una linea de código en ninguna de estas.

El patrón «Strategy» nos permite hacer una cosa que no podemos con «extends», definir la combinación que queramos de «Lista», «EstrategiaDeFiltrado» y «PostProcesador» en tiempo de ejecución.

Podríamos dejarlo aquí, pero he de comentar que «Strategy» comparte un problema con «Template Method». Con ambos necesitamos planear de antemano que «ejes» de extensibilidad o composición vamos a tener. Esto hace que la clase «ListaSencilla» no sea tan sencilla. Estamos en cierto modo violando el principio de única responsabilidad, ya que está claro que la responsabilidad de «ListaSencilla» es simplemente gestionar la lógica de «Lista». Además, estamos pagando una pequeña sobrecarga, al añadir lógica de llamada a filtrado y postprocesamiento en aquellos casos que realmente no la necesitamos. Cierto, es una sobrecarga muy pequeña, pero no deja de ser una mala señal. Yo lo que quiero es que «ListaSencilla» sea eso, «sencilla». Quiero este código:

public final class ListaSencilla implements Lista {
  private List datos = new ArrayList();
  public ListaSencilla() {
    super();
  }
  @Override
  public void insertar(String elemento) {
    datos.add(elemento);
  }
  @Override
  public void insertarVarios(Collection elementos) {
    for (String elemento : elementos)
      insertar(elemento);
  }
  @Override
  public String describirContenido() {
    StringBuilder descripcion = new StringBuilder("Contenidos: ");
    for (String elemento : datos) {
      descripcion.append("'");
      descripcion.append(elemento);
      descripcion.append("' ");
    }
    return descripcion.toString();
  }
}

¡ Ah, que gusto volver a los viejos tiempos donde las listas eran listas y nada más ! Pero necesitamos filtrado y postprocesamiento, ¿qué hacemos? Recurrir a otro patrón del «GoF», el «Decorator». Podemos hacer una clase que admita una «EstrategiaDeFiltrado», pero que no implemente nada de la lógica de la lista, sino que la delegue a un colaborador. O sea, la responsabilidad de esta nueva clase es simplemente orquestar a una «Lista» y a una «EstrategiaDeFiltrado», sin que ninguna de las dos sepa nada. Creemos, para tal efecto, la clase «ListaConFiltrado»:

public final class ListaConFiltrado implements Lista {
  private Lista lista;
  private EstrategiaDeFiltrado filtro;
  public ListaConFiltrado(Lista lista, EstrategiaDeFiltrado filtro) {
    super();
    this.lista = lista;
    this.filtro = filtro;
  }
  @Override
  public void insertar(String elemento) {
    if (!filtro.esInsertable(elemento))
      throw new ElementoRechazadoError(elemento);
    lista.insertar(elemento);
  }
  @Override
  public void insertarVarios(Collection elementos) {
    for (String elemento : elementos) {
      if (!filtro.esInsertable(elemento))
        throw new ElementoRechazadoError(elemento);
    }
    lista.insertarVarios(elementos);
  }
  @Override
  public String describirContenido() {
    return lista.describirContenido();
  }
}

También podemos hacer lo mismo y crear una «ListaConPostProcesamiento»:

public final class ListaConPostProcesamiento implements Lista {
  private Lista lista;
  private PostProcesador postProcesador;
  public ListaConPostProcesamiento(Lista lista, PostProcesador postProcesador) {
    super();
    this.lista = lista;
    this.postProcesador = postProcesador;
  }
  @Override
  public void insertar(String elemento) {
    lista.insertar(elemento);
    postProcesador.postProcesar(elemento);
  }
  @Override
  public void insertarVarios(Collection elementos) {
    lista.insertarVarios(elementos);
    for (String elemento : elementos)
      postProcesador.postProcesar(elemento);
  }
  @Override
  public String describirContenido() {
    return lista.describirContenido();
  }
}

¿Necesitaremos una clase «ListaConFiltradoYPostProcesamiento»? No, tal objeto se puede crear componiendo una «ListaConFiltrado» con una «ListaConPostProcesamiento», pasando la última como argumento al constructor de la primera. De hecho ahora podemos eliminar «NuncaFiltrar» y «NoProcesar» ya que no las usamos. Además volvemos a simplificar los tests de «ListaSencilla», ¡ ya no necesitamos dobles de prueba ! Por otro lado los tests de «ListaConFiltrado» y «ListaConPostProcesamiento» son muy sencillos, basta usar unos dobles de prueba para comprobar que las llamadas se delegan en el orden oportuno, y que no se llama ni a «insertar» ni a «insertarVarios» si el filtro nos devuelve «false».

Si os fijais estoy usando varios patrones que no usan herencia de implementación, sino una filosofía de diseño general llamada «composición y delegación«. Esta filosofía consiste en tener objetos sencillos, y bien encapsulados, que puedan ser combinados mediante composición, para obtener nueva funcionalidad. Tanto «Strategy», como «Decorator» usan esta técnica.

Siguiendo con el ejemplo, todos estos cambios afectan a nuestra factoría «NuevaLista», que ahora queda:

public final class NuevaLista {
  public NuevaLista() {
    super();
  }
  public Lista sencilla() {
    return new ListaSencilla();
  }
  public Lista auditable() {
    return new ListaConPostProcesamiento(sencilla(), new Auditar(
        new AuditorEstricto()));
  }
  public Lista sinElementosConEspacios() {
    return new ListaConFiltrado(sencilla(), new FiltrarElementosConEspacios());
  }
  public Lista sinElementosLongitudImpar() {
    return new ListaConFiltrado(sencilla(), new FiltrarElementosLongitudImpar());
  }
  public Lista auditableSinElementosConEspacios() {
    return new ListaConFiltrado(auditable(), new FiltrarElementosConEspacios());
  }
  public Lista auditableSinElementosLongitudImpar() {
    return new ListaConFiltrado(auditable(),
        new FiltrarElementosLongitudImpar());
  }
}

Ahora ese «Factory» es más sencillo y elegante que antes al haber eliminado los «setter». La clase «NuevaLista» simplemente compone o combina las distintas implementaciones de lista. Desgraciadamente cada vez que necesitemos una nueva combinación tenemos que tocar «NuevaLista» (o el XML de «Spring»). Para solventar ese problema podríamos aprovechar la potencia de «componer y delegar» y hacernos un mini DSL interno. Veamos cómo:

public final class FabricaDeListas {
  private List<EstrategiaDeFiltrado> filtros = new ArrayList();
  private List<PostProcesador> postProcesadores = new ArrayList();
  public FabricaDeListas() {
    super();
  }
  public FabricaDeListas(List filtros,
      List postProcesadores) {
    this();
    this.filtros.addAll(filtros);
    this.postProcesadores.addAll(postProcesadores);
  }
  public Lista fabricar() {
    Lista resultado = new ListaSencilla();
    for (PostProcesador procesador : postProcesadores)
      resultado = new ListaConPostProcesamiento(resultado, procesador);
    for (EstrategiaDeFiltrado filtro : filtros)
      resultado = new ListaConFiltrado(resultado, filtro);
    return resultado;
  }
  public FabricaDeListas conPostProcesador(PostProcesador procesador) {
    FabricaDeListas fabrica = new FabricaDeListas(this.filtros,
        this.postProcesadores);
    fabrica.postProcesadores.add(procesador);
    return fabrica;
  }
  public FabricaDeListas conAuditor(Auditor auditor) {
    return conPostProcesador(new Auditar(auditor));
  }
  public FabricaDeListas conAuditoriaEstricta() {
    return conAuditor(new AuditorEstricto());
  }
  public FabricaDeListas conFiltro(EstrategiaDeFiltrado filtro) {
    FabricaDeListas fabrica = new FabricaDeListas(this.filtros,
        this.postProcesadores);
    fabrica.filtros.add(filtro);
    return fabrica;
  }
  public FabricaDeListas sinElementosLongitudImpar() {
    return conFiltro(new FiltrarElementosLongitudImpar());
  }
  public FabricaDeListas sinElementosConEspacios() {
    return conFiltro(new FiltrarElementosConEspacios());
  }
}

Simplemente usamos un patrón «Value Object» mezclado con un «Builder». La ventaja de este enfoque es que sea cual sea la combinación que quiera la puedo obtener fácilmente con «FabricaDeListas», sin tener que tocar una sola linea de código dentro de «FabricaDeListas». Puedo hacer cosas como:

Lista auditadaSinElementosConEspacios = fabrica
                                           .sinElementosConEspacios()
                                           .conAuditoriaEstricta()
                                           .fabricar();

Mucho más legible, ¿no?

Tras este largo camino, ¿alguien considera que el código resultante es ilegible, poco mantenible o poco potente? ¿Para que nos sirvió el «extends» si no fue sólo para darnos dolores de cabeza? Hemos aprendido una valiosa lección, cualquier diseño basado en «extends» y «Template Method» puede ser refactorizado en un «Strategy», y éste a su vez en un «Decorator». Por lo tanto, ¿para que perder el tiempo usando «extends» si puedo hacer un «Strategy» o un «Decorator»? Al fin y al cabo el diseño basado en «composición y delegación» es más seguro y más potente.

Sin embargo aquí entra en juego las herramientas que te da el lenguaje. Una pista nos la puede dar la clase «Auditar», donde estamos creando una clase para simplemente adaptar la interfaz «PostProcesador» a «Auditor», pero realmente no hay ninguna lógica ahí. A mi me parece que esa clase es un buen desperdicio de código, pero la verdad es que no se puede hacer mucho más en JAVA. Otro ejemplo es el método «describirContenido» en «ListaConFiltrado» y «ListaConPostProcesamiento». Ese método no hace nada, sólo delega la llamada sin añadir lógica, ¡ qué desperdicio de líneas de código ! Tampoco podemos hacer mucho más en leguaje JAVA. Afortunadamente tenemos IDEs como «Eclipse», «NetBeans» o «IntelliJ» que nos proporcionan el poderoso wizard «Generate Delegate Methods…», y no las tenemos que escribir.

Cuando veo este tipo de detalles es cuando pienso que JAVA no está envejeciendo muy bien. Gracias señores estandarizadores de JAVA, perdieron ustedes una gran oportunidad de mejorar el lenguaje cuando lanzaron JAVA 5. En vez de parir ese aborto de «Map<? extends Enumerable, List<? extends X>>», debían haber mejorado el lenguaje para añadir una forma sencilla de aplicar la técnica de «composición y delegación», ¿tal vez una palabra clave «delegate»? ¿O soporte para funciones como primer ciudadano? No, ¿para qué? Los genéricos son mucho más «molones» y para reutilizar código ya tenemos el «extends». Todo esto es mucho más simple de hacer en JavaScript, Groovy o Ruby. A ver si en JAVA 7 o JAVA 8 se ponen las pilas.

¡ Gracias por aguantar hasta el final de este post tan largo !


Hola de nuevo, como vimos en el anterior post la herencia de implementación puede ser un verdadero problema. Sí, es verdad, existen técnicas para diseñar nuestras clases para que puedan ser reutilizadas mediante herencia. Notad la cosa tan fea que acabo de decir. Si quiero que mi clase sea reutilizada, tengo que diseñarla de antemano, es decir, no importa para nada la interfaz de la clase, ni los tests que tenga sobre esta, ni la especificación del contrato. Si quiero reutilizar utilizando «extends» tengo que saber como está implementada la clase internamente, incluyendo todas las cosas «private» que tenga por dentro. A algunos les parecerá de lo más normal, a mi me da que pensar, ¿y entonces, para que leches sirve la encapsulación?

En cualquier caso voy a profundizar un poco en como hacer las cosas «bien» con la herencia de implementación, para ver las limitaciones de la técnica, y para ver si me quito el San Benito de programador petardo.

Tras reflexionar sobre el problema de mantenibilidad que tienen con la clase «Lista», el equipo decide contratar a un consultor externo, y tras convencer a la mesa de compras de que paguen la estratosférica tarifa de 45 euros/hora, consiguen contratar a uno de los más reputados expertos locales. Haciendo honor a sus honorarios, el experto diagnostica y resuelve el problema en un santiamén (lástima que cobre jornada o fracción). «Pero señor@s», dice el experto, «¿es que nunca habéis leído el GoF?», continua, «aquí os pongo un patrón Template Method que os vais a chupar los dedos». Ni corto ni perezoso modifica el código, y ListaSencilla le queda así.

public class ListaSencilla implements Lista {
  private List datos = new ArrayList();
  public ListaSencilla() {
    super();
  }
  @Override
  final public void insertar(String elemento) {
    datos.add(elemento);
    procesamientoAdicionalTrasInsertarElemento(elemento);
  }
  @Override
  final public void insertarVarios(Collection elementos) {
    for (String elemento : elementos)
      insertar(elemento);
  }
  /**
   * Este método es invocado cada vez que un nuevo elemento
   * es insertado en la Lista
   * @param elemento El elemento que acaba de ser insertado
   */
  protected void procesamientoAdicionalTrasInsertarElemento(String elemento)
  {
    // Intencionadamente en blanco, es un "hook" para sobreescribir
  }
  @Override
  public String describirContenido() {
    StringBuilder descripcion = new StringBuilder("Contenidos: ");
    for (String elemento : datos) {
      descripcion.append("'");
      descripcion.append(elemento);
      descripcion.append("' ");
    }
    return descripcion.toString();
  }
}

Nótese el uso de la palabra clave «final». Esta palabra clave se puede poner a nivel de método, o mejor, a nivel de clase, indicando que no se puede sobrescribir el método o hacer extends de la clase. La palabra clave «final» es un gran invento de JAVA, pero claro,  hay que leer entre líneas: un lenguaje que tiene herencia de implementación, pero que además tiene una palabra clave que prohíbe la herencia, muy sospechoso. En realidad los diseñadores de JAVA sabían muy bien que la reutilización de código mediante herencia de implementación es peligrosa. Conscientes de ellos, añadieron «final», para dar la capacidad al desarrollador de marcar una clase o método con «¡Peligro, no heredar o sobrescribir!», en el caso de que no hubieran diseñado de antemano la clase para herencia. Desgraciadamente no he visto que mucha gente use «final».

Siguiendo con la historia, al usar «final» y «Template Method» en conjunción con el método protegido «procesamientoAdicionalTrasInsertarElemento», se desactiva cualquier problema. Por un lado no podemos sobrescribir los métodos peligrosos, ya que son «final». Por otro lado el implementador de la clase diseña su código de tal forma que si quiero reutilizar la clase, no lo pueda hacer de cualquier forma, sino sólo sobrescribiendo el método protegido a tal fin. Con este cambio, la clase «ListaAuditable» queda como sigue:

public class ListaAuditable extends ListaSencilla {
  private Auditor auditor;
  public ListaAuditable(Auditor auditor) {
    super();
    this.auditor = auditor;
  }
  @Override
  protected void procesamientoAdicionalTrasInsertarElemento(String elemento)
  {
    auditor.elementoInsertado(elemento);
  }
}

Da la impresión de ser un código bastante elegante. Desde luego ha merecido la pena pagar la exorbitante tarifa del consultor externo. Sólo hay un problema pequeñito, el test de «ListaSencilla» se ha complicado. Claro, ahora no sólo hay que testear el contrato «Lista» en «ListaSencilla», que es el que define como se usa la clase por parte de los consumidores de esta. Ahora hay otro contrato, representado por el método protegido, entre «ListaSencilla» y todas las clases que la extiendan. Este contrato es «en negro», ya que no es público, sino «protegido». Necesitamos un test que pruebe que el método protegido es llamado en los momentos adecuados y con los parámetros adecuados. Pero este contrato no está en una interfaz de un colaborador, sino en un método «protected», ¿cómo hacemos el test? Afortunadamente con un poco de programación «clever» se puede terminar haciendo testing de esto. Lo dejo como ejercicio al lector.

Otro pequeñito problema es que alguien siga pensando que no tiene por que entender la implementación de la superclase, que le vale sólo entendiendo el contrato público (pobrecito), y al heredar haga cosas como esta:

public class ListaAuditablePetarda extends ListaSencilla {
  private Auditor auditor;
  public ListaAuditablePetarda(Auditor auditor) {
    super();
    this.auditor = auditor;
  }
  @Override
  protected void procesamientoAdicionalTrasInsertarElemento(String elemento)
  {
    super.insertar(elemento); // Ouch !!!
    auditor.elementoInsertado(elemento);
  }
}

¡ Ay, que cosa más fea ! ¿Es que nadie se lee el Javadoc? Obviamente nadie debería usar super para llamar a un método peligroso (final), sólo sobrescribir métodos protegidos o métodos públicos no peligrosos (no final). Bueno, a parte de estos problemitas subsanables con un poco de «hacking», y despedir al inútil que cometió la anterior tropelía, parece que la técnica funciona.

Pasa el tiempo y aparece otro problema con este enfoque. ¿Qué ocurre si queremos hacer la extensión de una clase de una forma no prevista de antemano? No puedes. Tienes que volver a modificar la clase padre para añadir extensibilidad en el nuevo «eje» que te haga falta. Por supuesto en cualquier desarrollo serio, ha habido una extensa e intensiva fase de análisis funcional y diseño técnico, y esas cosas no pueden pasar, ya que se han cubierto todos los posibles cambios. Desgraciadamente nuestros amigos no son tan profesionales y usan una cosa llamada «agile» que les impide hacer un buen análisis y diseño «up-front» como dios manda, y no tienen muy claro las necesidades de diseño futuro de su sistema.

Nuestros sufridos desarrolladores se encuentran con un nuevo requisito, ahora resulta que algunas especializaciones de «ListaSencilla» pueden rechazar un elemento, y negarse a insertarlo si cumple alguna característica concreta. Desgraciadamente en las clases hijas de «ListaSencilla» no pueden añadir esta funcionalidad, deben abrir su implementación, y siguiendo «Template Method» añadir un punto de extensión más.

public class ListaSencilla implements Lista {
  private List datos = new ArrayList();
  public ListaSencilla() {
    super();
  }
  @Override
  final public void insertar(String elemento) {
    if (!esInsertable(elemento))
      throw new ElementoRechazadoError(elemento);
    datos.add(elemento);
    procesamientoAdicionalTrasInsertarElemento(elemento);
  }
  @Override
  final public void insertarVarios(Collection elementos) {
    for (String elemento : elementos) {
      if (!esInsertable(elemento))
        throw new ElementoRechazadoError(elemento);
    }
    for (String elemento : elementos)
      insertar(elemento);
  }
  /**
   * Este método es invocado para pedir permiso sobre si un elemento
   * puede ser insertado o no
   *
   * @param elemento
   *        El elemento que queremos insertar
   * @return si se puede insertar o no
   */
  protected boolean esInsertable(String elemento) {
    return true;
  }
  /**
   * Este método es invocado cada vez que un nuevo elemento
   * es insertado en la Lista
   *
   * @param elemento
   *        El elemento que acaba de ser insertado
   */
  protected void procesamientoAdicionalTrasInsertarElemento(String elemento)
  {
    // Intencionadamente en blanco
  }
  @Override
  public String describirContenido() {
    StringBuilder descripcion = new StringBuilder("Contenidos: ");
    for (String elemento : datos) {
      descripcion.append("'");
      descripcion.append(elemento);
      descripcion.append("' ");
    }
    return descripcion.toString();
  }
}

Bueno, no duele tanto, al fin y al cabo el código parece razonablemente limpio y «ListaAuditable» ni se ha enterado. Ahora implementamos el requisito, realmente ocurre que pueden existir tres tipos de listas auditables, aquellas que no admiten elementos que contengan espacios y aquellas que sólo admiten elementos de longitud par. Bueno, a implementar. La cosa queda como sigue:

public class ListaAuditableConElementosSinEspacios extends ListaAuditable {
  public ListaAuditableConElementosSinEspacios(Auditor auditor) {
    super(auditor);
  }
  @Override
  protected boolean esInsertable(String elemento) {
    return !elemento.contains(" ");
  }
}

Y también:

public class ListaAuditableConElementosLongitudPar extends ListaAuditable {
  public ListaAuditableConElementosLongitudPar(Auditor auditor) {
    super(auditor);
  }
  @Override
  protected boolean esInsertable(String elemento) {
    return elemento.length() % 2 == 0;
  }
}

Ok, no esta nada mal, casi podemos cantar victoria, ¡además hemos reutilizado «ListaAuditable»!

Pasa el tiempo, y con él llegan cambios, e inevitablemente nos llega otro requisito. Resulta que las listas que no son auditables también pueden existir en la forma de listas que no admiten elementos con espacios, y las que sólo admiten elementos de longitud par ¡Qué dura es la vida del desarrollador! De nuevo al código, queda lo que sigue:

public class ListaConElementosSinEspacios extends ListaSencilla {
  public ListaConElementosSinEspacios() {
    super();
  }
  @Override
  protected boolean esInsertable(String elemento) {
    return !elemento.contains(" ");
  }
}

Y también:

public class ListaConElementosLongitudPar extends ListaSencilla {
  public ListaConElementosLongitudPar() {
    super();
  }
  @Override
  protected boolean esInsertable(String elemento) {
    return elemento.length() % 2 == 0;
  }
}

¡Han aparecido otras dos clases! ¡Ya tenemos 6 implementaciones de «Lista»! Bueno, al menos son cortitas y sencillas, y estamos reutilizando un montón. El problema es que estamos a empezar a detectar un tufillo a violación del DRY. De hecho es la misma implementación de antes pero heredando de «ListaSencilla» en vez de «ListaAuditable» ¿Quién dijo que el copy&paste era malo? Quizás si en vez de usar «extends» usáramos otra cosa… nada, todo el mundo sabe que la herencia es «esencial» a la OO, su «killer feature», mejor seguimos con el «Template Method», y no liamos más la cosa.

Seis meses más tarde tenemos 60 clases «Lista». Resultó que salieron cinco formas más de filtrar los elementos de las listas, y encima además de listas «auditables» y «sencillas», tenemos listas «lazy», listas «persistentes», etc. En fin, que necesitamos una clase por cada combinación posible. La verdad es que esto ya no parece tan limpio.

Una última reflexión. Para el que sea propenso a pensar en cosas inútiles, aquí lanzo una pregunta, ¿cómo es mejor implementar ListaAuditableConElementosLongitudPar? ¿Heredando de ListaConElementosLongitudPar o heredando de ListaAuditable? ¿Da igual? Y en caso de que sea igual, ¿no parece un accidente histórico el decidir implementarla de una forma y no de otra, y no una decisión de diseño guiada por criterios de ingeniería del software? Inquietante, al menos para mi.

Reconozco que este es un ejemplo un poco «cogido por pinzas», pero no me negaréis que ilustra un problema real, que tal vez muchos estáis reconociendo de haberlo sufrido. Esta explosión combinatoria de clases es algo que se conoce también desde antiguo, y está ligado al hecho de que tenemos herencia de implementación simple. Si alguno está pensando en solucionarlo con herencia de implementación múltiple, nada vosotros mismos. Si no habéis tenido bastante con la herencia simple, ahora si que os podéis meter en un buen lío. Está claro que el verdadero problema reside en usar «extends» como mecanismo de  reutilización de código.

Resumiendo:

  • Puedo reutilizar código con «extends», pero tengo que diseñar de antemano para ello.
  • Al diseñar de antemano, sólo puedo reutilizar aquello que está pensado para serlo, y no cualquier característica pública de la interfaz de la clase. Si necesito reutilizar mediante «extends» alguna característica de la clase que no esté diseñada para ello, pues o me aguanto, o no uso «extends» o simplemente abro la clase padre y la modifico (muy SOLID no es, no).
  • El que reutilice con «extends» una clase debe conocer si ésta está diseñada para ello, y por lo tanto conocer la implementación interna de la clase padre.
  • La forma más común (no la única), es usar un patrón «Template Method» de GoF, pero esto dificulta el testing, ¿cómo pruebo los métodos protected?
  • Algún programador petardo, que no se haya leído nuestro excelente Javadoc de los métodos «protected», puede acabar invocando al «super» y terminar con errores en las nuevas clases, errores que a veces son sutiles de detectar.
  • Esta forma de reutilizar código está sujeta además a la explosión combinatoria de clases.

Algunos argumentaron en el post anterior que «extends» no sirve como mecanismo de reutilización de código, sino como mecanismo de especialización. O sea, para especializar clases pero sin reutilizar código. Pues vaya tela, ¡yo pensaba que lo que mantenían relaciones de especialización eran las interfaces!. Y que alguien me diga, ¡ para que sirve la herencia de implementación si no es para reutilizar código ! ¡Para que quiero especializar una clase sin reutilizar su código! ¡ Quiero un ejemplo concreto !

¿Existe alguna forma mejor de reutilizar código? La respuesta es sí, pero viene con un precio a pagar. A algunos, como a mi, este precio le parece pequeño, pero a otros les parece enorme. Lo veremos en el siguiente post.


Hola a todos, este post me lo venía pidiendo el cuerpo desde hace tiempo. Es común oírme decir que la herencia de implementación («extends» para los javeros) es una mierda, y cada vez que lo digo veo caras de estupefacción e incluso recibo alguna que otra crítica. La verdad es que no lo entiendo bien ya que esto se sabe desde hace bastante tiempo, es más hasta tiene un nombre: «problema de la clase base frágil», aunque yo prefiero decir que «extends es una mierda». Incluso, algunos dicen que James Goslin, el padre de JAVA, reconoció que incluir «extends» en el lenguaje fue un gran error. Yo considero que «extends» no tiene nada que ver con la OO (al menos no con el concepto original), sino que se incluyó porque para algo tenían que servir las clases, y se pensó que se podrían usar para reutilizar código mediante la herencia de implementación.

Vamos al lío, os voy a contar una historia. Suponed la siguiente interfaz:

public interface Lista {
	void insertar(String elemento);
	void insertarVarios(Collection elementos);
	String describirContenido();
}

Se trata de una interfaz sencilla que representa una lista, con dos métodos para añadir contenido. Veamos ahora una implementación de tal interfaz:

public class ListaSencilla implements Lista {
	private List datos = new ArrayList();

	public ListaSencilla() {
		super();
	}

	@Override
	public void insertar(String elemento) {
		datos.add(elemento);
	}

	@Override
	public void insertarVarios(Collection elementos) {
		for (String elemento : elementos)
			this.insertar(elemento);
	}

	@Override
	public String describirContenido() {
		StringBuilder descripcion = new StringBuilder("Contenidos: ");
		for (String elemento : datos) {
			descripcion.append("'");
			descripcion.append(elemento);
			descripcion.append("' ");
		}
		return descripcion.toString();
	}
}

¿Sencillo verdad? Es una implementación muy directa y no tiene mayor misterio. Ahora supongamos que a otro miembro del equipo, que no escribió la clase «ListaSencilla», le encargan desarrollar una implementación de «Lista» que cada vez que se añada un elemento a ésta, informe a un objeto auditor de tal suceso. Como nuestro desarrollador está enfocado en el objetivo de esta nueva clase, que es informar al auditor, y no le interesa volver a reimplementar y duplicar la lógica de inserción de datos, decide reutilizar código. ¿Qué mejor manera de utilizar el venerable «extends»? Al fin y al cabo, la herencia de implementación es la verdadera ventaja de la OO y su mecanismo más potente para reutilizar código. Mirando la especificación de «Lista» y conociendo que ya hay una implementación «ListaSencilla» disponible, le sale esto:

public class ListaAuditable extends ListaSencilla {
	private Auditor auditor;

	public ListaAuditable(Auditor auditor) {
		super();
		this.auditor = auditor;
	}

	@Override
	public void insertar(String elemento) {
		super.insertar(elemento);
		this.auditor.elementoInsertado(elemento);
	}

	@Override
	public void insertarVarios(Collection elementos) {
		super.insertarVarios(elementos);
		for (String elemento : elementos)
			this.auditor.elementoInsertado(elemento);
	}
}

El desarrollador ni se molesta en probarlo, obviamente va a funcionar, él ha respetado el contrato de la interfaz y está usando a los amiguetes «extends» y «super». Más tarde le llega un bug, ¡resulta que el auditor es notificado por cada elemento por duplicado! Desconcertado nuestro programador recurre a esa poderosa arma de la ingeniería del software, el depurador, y consigue aclarar el misterio. La implementación del método «insertarVarios» en la superclase llama internamente a «insertar», que como está sobreescrito en la clase hija también notifica al auditor, pero claro, «insertarVarios» de la clase hija también notifica al auditor por su cuenta, y de ahí la doble notificación al auditor. Mas claro agua, y si no lo habéis entendido, ¡depurad!

El desarrollador aprende la valiosa lección de que no sólo debe saber el contrato del código que quiere reutilizar, sino que debe analizar con detalle como está diseñada internamente la clase a reutilizar. Ya sabía él que todo esto del diseño por contrato y las interfaces no eran más que paparruchas para vender libros y cursos. Tras pensar detenidamente cambia el código y le queda:

public class ListaAuditable extends ListaSencilla {
	private Auditor auditor;

	public ListaAuditable(Auditor auditor) {
		super();
		this.auditor = auditor;
	}

	@Override
	public void insertar(String elemento) {
		super.insertar(elemento);
		this.auditor.elementoInsertado(elemento);
	}
}

Genial, bug resuelto de la mejor manera posible: borrando código. Más contento que unas castañuelas, el desarrollador se va a casa con la satisfacción del buen trabajo cumplido.

Pasa el tiempo, y un desarrollador senior, revisando la clase «ListaSencilla», e ignorante de que esta clase tiene más descendientes que Gengis Khan, decide optimizar el método «insertarVarios». Decide ahorrarse el bucle, y que leches, reutilizar código de la clase «ArrayList» que para eso viene en la JDK, estos desarrolladores junior sufren del síndrome NIH… La nueva versión:

public class ListaSencilla implements Lista {
	private List datos = new ArrayList();

	public ListaSencilla() {
		super();
	}

	@Override
	public void insertar(String elemento) {
		datos.add(elemento);
	}

	@Override
	public void insertarVarios(Collection elementos) {
		datos.addAll(elementos);
	}

	@Override
	public String describirContenido() {
		StringBuilder descripcion = new StringBuilder("Contenidos: ");
		for (String elemento : datos) {
			descripcion.append("'");
			descripcion.append(elemento);
			descripcion.append("' ");
		}
		return descripcion.toString();
	}
}

¡ Qué sencillez ! ¡ Qué elegancia ! No por nada es un «lead developer» que se conoce la JDK al dedillo. Otro que se va para casa inconsciente de la que acaba de armar. ¿Por qué debería preocuparse? Al fin y al cabo el contrato de la interfaz «Lista» se cumple, y para demostrarlo tiene un test automatizado de hermoso color verde. Mientras tanto, la «ListaAuditable» y posiblemente muchas más subclases de «ListaSencilla» comienzan a fallar sin previo aviso.

Al día siguiente, el «lead developer» decide arreglar el problema, mira a ver el código de «ListaAuditable» y lo solventa en menos que canta un gallo. El código queda así:

public class ListaAuditable extends ListaSencilla {
    private Auditor auditor;

    public ListaAuditable(Auditor auditor) {
        super();
        this.auditor = auditor;
    }

    @Override
    public void insertar(String elemento) {
        super.insertar(elemento);
        this.auditor.elementoInsertado(elemento);
    }

    @Override
    public void insertarVarios(Collection elementos) {
        super.insertarVarios(elementos);
        for (String elemento : elementos)
            this.auditor.elementoInsertado(elemento);
    }
}

¿Os resulta familiar 😉 ?

«Tonto desarrollador junior», piensa el lead developer, «se nota que en vez de fijarse en la interfaz y los tests, se ha mirado la implementación de la superclase, y por ahorrarse un método la que liado…». En fin, muy triste 😦

Algunos de vosotros pensará que he hecho trampas, que no he usado «bien» la herencia de implementación y que soy un petardo de programador. En el siguiente post explicaré como hacerlo correctamente. Pero ésto no es lo importante, lo que quiero mostrar es que tenemos un mecanismo de reutilización de código, la herencia de implementación, que es más peligrosa que una caja de bombas. Yo prefiero no usar un mecanismo con el que tengo que andarme con pies de plomo, aunque me haya leído y entendido el contrato del código que quiera reutilizar. La herencia de implementación no es segura porque rompe la encapsulación, y necesito saber cómo está implementada la clase padre para poder heredar de ella de forma segura. Todo esto se produce porque mezcla el estado e implementación privada de la superclase con la de la clase hija en una única instancia, en vez de mantenerlas separadas. ¿No sería mejor tener un mecanismo de reutilización de código que siempre fuera seguro? Sí, claro que existe, y lo veremos más adelante.