Sobre WCF, DTO, EF, POCO y los principios de la programación

Creo que este es el primer artículo que vamos a ver en programandonet a modo editorial. Esto es el resultado de una conversación en el grupo de arquitectos .net de LinkedIn, en la que se hablaba sobre usar POCOs de entity framework como DTO para servicios WCF, que se inició hace unos días. Y es que, no me hubiera gustado que algunas conclusiones y escritos se perdieran en este grupo y por eso hemos creído interesante publicarlas aquí, para tenerlas a mano.

Para aquellas personas que no estén familiarizadas con estas siglas haremos un breve glosario:

Si retomamos la cuestión inicial, la primera conclusión que desarrollamos fue que: si usas objetos POCO de Entity Framework como DTO, le estás dando dos responsabilidades a tu objeto. Por un lado sirve de entidad del ORM y por otro de transferencia de WCF. Por lo que estarías incumpliendo la S de los principios SOLID: Single responsibility principle (SRP). Por lo tanto la respuesta es que no deberíamos hacerlo.

Además al darle estas dos responsabilidades a un objeto, obligas a que tus servicios envíen "toda" la información que almacenas en la base de datos, y quizá solo hace falta una parte de esa información. Entonces corres el riesgo de enviar demasiada información con un servicio, o demasiada poca, haciendo necesario llamar a dos métodos diferentes. Es decir, un alto riesgo de crear servicios poco eficientes.

Pero existe el contexto en el que los datos almacenados sean exactamente los mismos que queremos enviar en nuestros servicios. En este sentido @jc_quijano defendió una postura que se amparaba en el principio de la programación KISS (Keep It Simple, Stupid!). Es decir, es más rápido y fácil desarrollar un POCO para EF y reutilizarlo como DTO para nuestros servicios. Además, si se añaden DTOs a este concepto, estamos añadiendo una capa nueva de complejidad a la aplicación. Y como no es la estructura de los objetos lo que me definen su responsabilidad, si no el uso que le doy a esos objetos, estructuralmente, tampoco estoy rompiendo ningún principio SOLID.

Esta filosofía me parece correcta a la hora de desarrollar aplicaciones con un ciclo de vida muy corto. Como puede ser una herramienta que ayuda a corregir un problema puntual y que una vez corregido, podemos deshacernos de esta. De esa forma no tendría un mantenimiento largo y no se convertiría en un problema el acoplamiento que crea usar un objeto para dos tareas diferentes.

Y es que KISS no es un principio más importante que el SRP o que cualquiera de SOLID. Ni un principio de SOLID es más importante que cualquier otro principio (incluido KISS). Los principios SOLID te llevan a programar mejor e indirectamente a empezar a seguir otros principios como KISS o YAGNI. Mi opinión y recomendación es seguirlos todos.

El principio KISS no debe confundirse con programar poco, si no que lo que programes hacerlo simple. No añadir 20 capas a una solución que con 2 va de sobra. Pero tampoco hacer una clase maestra que lleve todo el peso de la aplicación y que tenga 100 propiedades.

Vamos a proponer un ejercicio de imaginación para desarrollar un ejemplo:

Si tenemos nuestro proyecto en orden (como fan confeso de TDD), con los test unitarios correspondientes, podemos ser capaces de medir fácilmente la complejidad de la solución. Estará determinada por el número de tests unitarios que habrá que modificar en el momento en el que se produzca un cambio.

Imaginemos un escenario en el que por alguna razón, nuestro servicio tiene que cambiar a consecuencia de una nueva especificación de nuestro product owner. Algo que creemos que es común en los proyectos con un ciclo de vida medio/largo.

Si tenemos la solución que usa objetos DTO específicos, este cambio podría implicar modificar un test unitario y a su vez añadir una propiedad a nuestro DTO. Pero si nuestra solución no sigue el SRP, es decir, que usamos los objetos POCO también como DTOs, el escenario es diferente. Cambiaremos un test, después el DTO/POCO, y después nos fallarían un par de tests unitarios (por decir un número) que están relacionados con la forma de almacenar los datos. Y para terminar tendríamos que añadir, sin que tenga por qué ser necesario, un campo más a nuestra base de datos. Ahora imaginemos las consecuencias de este cambio multiplicada por el número de cambios que suceden en una aplicación cualquiera (que no tenga un corto ciclo de vida, claro :D).

La conclusión que podemos sacar de todo esto es que el código que ha resultado más fácil de seguir para el resto de los programadores, más mantenible y en consecuencia más simple; ha sido el desacoplado con sus DTOs por un lado y su EF por otro. Tener una buena estructura es programar de forma simple. Si cada objeto hace lo que debe hacer, no es difícil entender una aplicación. Si las clases empiezan a tener más de una responsabilidad, podremos encontrar resultados inesperados cuando hacemos un cambio.

El principio de responsabilidad única nos ha llevado a hacer un código más simple, aunque hayamos que tenido que programar más líneas de código. Por lo que personalmente, prefiero seguir todos los principios y hacer aplicaciones que tengan objetos POCO por un lado y DTO por otro.

 

Y por si seguir los principios de la programación no parece un argumento lo suficiente sólido para argumentar separar estas dos responsabilidades en dos objetos, una reflexión personal: No creo que un ORM sea la solución que deba ser "visible", si no más bien una "pasarela", porque:

De estas dos premisas, la deducción que he sacado es que el acceso a datos tiene que ser una parte "aislada" del resto de la aplicación. Está en una capa de nuestra arquitectura llamada "persistencia". Y tendremos abstrayéndonos de este ORM a un modelo de dominio (donde aplicaremos normas de negocio) y/o de servicios (DTO, para comunicárselo al "mundo"). De esta forma cada parte de la aplicación hará lo que debe de la forma más eficiente para su cometido...

Eso si, como arquitectos debemos encontrar la solución que más se adecua al escenario de nuestro proyecto. No sobre-dimensionar un programa que nos sirve para calcular números primos con 5 capas. Pero tampoco crear una aplicación que va a ser mantenida/expandida durante 20 años y esté totalmente acoplada.

Ahora bien, todo esto es una opinión que puede ser modificada por unos argumentos que consideremos mejores:

¿Qué opináis vosotros?


Podeís seguir la conversación en LinkedIn a ver en que termina derivando ya que creo que es muy interesante...