¿DDAU?

Ember ha ido formalizando la adopción de un flujo de datos unidireccional, que llegará a remover los controllers y la unión en dos vías (two-way bindings) en favor de una arquitectura basada en componentes. En este artículo exploramos qué es DDAU y por qué utilizarlo es importante desde ahora para hacer nuestras aplicaciones más robustas.

Foto por Tom Watchel

— Creo que tu aplicación no tiene una arquitectura DDUP.

Ese fue el diagnóstico de un amigo tras presentarle unos problemas que estaba teniendo con computed properties y promises (TIP RÁPIDO: no intenten juntar ambos; claro, a menos que el dolor y la inestabilidad sean lo suyo).

Pero, ¿DDUP?

Resulta que DDUP (Data down, actions up) es un principio básico del diseño de Ember que poco a poco se ha ido formalizando en el framework. No es nada del otro mundo, y Ember nos ayuda a seguirlo, pero es importante tenerlo muy en cuenta para tener aplicaciones mantenibles y escalables.

¿Por qué estamos hablando de esto?

A medida que una aplicación crece, sus interacciones y dependencias van aumentando y haciéndose cada vez más complejas.

Imaginemos una aplicación que maneja acciones, carga datos y los guarda entre rutas, controladores y componentes de forma irregular: al principio seremos capaces de recordar más o menos qué hace cada cosa. Pero si la volvemos a ver en unos meses quizá ya lo habríamos olvidado.

O qué pasará si necesitamos añadirle una nueva interacción, y otra, y otra. Y entonces cambian los datos que recibimos del servidor y la forma de desplegarlos, o se debe integrar alguien más al equipo de desarrollo.

Muchas cosas pueden cambiar a lo largo de un proyecto, y no queremos que cambiar nuestra aplicación sea como abrir la caja de pandora. Si no tenemos una estrategia bien definida para manejar el flujo de información, terminaremos en un espagueti difícil de entender, debuggear y extender. Por eso es importante tomar en cuenta este principio de diseño, que además nos ayudará a estar en sincronía con el desarrollo de Ember.

La data baja, las acciones suben

Flujo de datos DDAU, trabajo bajo licencia de Creative Commons Attribution 4.0 International.

Este principio de diseño se refiere al flujo de datos y eventos en nuestra aplicación. Básicamente se trata de cargar la información desde el servidor, por ejemplo, y hacerla fluir hacia abajo en la jerarquía de nuestra aplicación (la data baja). Quienes están abajo en la jerarquía no deben modificar esta información directamente, sino que deben limitarse a informar a su superior cuando deba registrarse un cambio (las acciones suben).

Vamos a aterrizar esto: ¡tiempo del código!

Utilicemos un ejemplo muy sencillo para ilustrar estos conceptos. Digamos que tenemos el modelo carro:

Usamos Ember 2.3.0 y Ember Data 2.3.0.

Nota: operaciones asíncronas

Cargar datos del servidor es una operación asíncrona, lo cual quiere decir—en términos sencillos—que la aplicación continúa la ejecución de código aunque los datos no estén disponibles aún. Por ejemplo, si queremos cargar todos los carros desde el servidor y luego imprimir en consola cuántos hay, esto no funcionaría:

const carros = this.store.findAll('carro');
console.log(carros.get('length')); //Valor incorrecto

Carros es una promesa, no tiene directamente el valor de lo que retorna el servidor; además, para el momento en que se ejecuta el console.log no se ha terminado de cargar los datos del servidor. No entraremos en detalle en este artículo sobre las promesas; basta saber que en la variable carros se resolverá la petición con los datos del servidor hasta que estén listos, pero la ejecución del código continuará independientemente de eso.

Un lugar acogedor para las promesas

El lugar canónico para cargar datos del servidor es el modelo de la ruta. Solo el model hook está consciente que retornará una promesa y ember proporciona mecanismos y herramientas muy convenientes para manejar su naturaleza asíncrona. Veamos una posible solución para imprimir en consola la cantidad de carros:

Además, Ember pondrá el modelo en el controller automáticamente en el hook setupController (No necesitamos agregar este método a menos que requiramos cambios.), realizando así la parte de data down por nosotros. Por tanto, ya tendremos disponible esta información en nuestra template y podemos pasarla a los componentes que tengamos en ella:

Sobre el componente mostrar-carro

Supongamos que nuestro componente muestra el color editable y un botón de eliminar:

Una primera aproximación podría decirnos que definamos la lógica del componente como:

Parece que es lo más simple porque podemos cambiar el color del carro directamente y eliminar el carro desde el componente. Pero esto es incorrecto en la mayoría de casos. Esto es precisamente lo que buscamos evitar utilizando DDAU: mezclar las funciones. Pensemos también que de esta forma no tenemos una forma de saber cuando guardar los cambios del usuario.

Una forma DDUP

Una solución DDAUP sería definir nuestro componente para que notifique de los cambios al controller. Para ello vamos a utilizar el plugin de one-way-controls en vez de utilizar el default input helper, pues aún tiene two-way-bindings.

Para este caso puede verse que ya no necesitamos manejar las acciones en el componente, pues solo utilizaremos directamente las acciones que se pasan al componente.

También debemos actualizar la template de la ruta para pasarle al componente las acciones que debe ejecutar:

Y registrar estas acciones en el controlador:

Ahora controlamos la forma en que se guardan los cambios en el carro desde un solo punto. Como bonus tenemos que solo vamos a guardar en el servidor los cambios del usuario cada 300ms utilizando debounce, en vez de hacerlo cada vez que presiona una tecla.

Demo completo

El código de la aplicación completo para este sencillo puede encontrarse en este repositorio para más referencias:

¿El futuro?

Actualmente los componentes por default tienen un two-way-binding, por el que los cambios en la data que se hacen en el componente se reflejan en su padre. Sin embargo, este comportamiento será retirado en algún momento pues es difícil de mantener y debuggear, como hemos explorado en este artículo.

Además, estamos migrando a una arquitectura basada en componentes, por lo que es importante tener en cuenta cómo se comportan y la mejor forma de utilizarlos. En especial porque serán, en cierto modo, el reemplazo de los controllers.

¿Correcciones, sugerencias, dudas?

Como siempre, agradezco sus correcciones y sugerencias. También intentaré resolver sus dudas. Si hay algún tema en particular que quieres para el siguiente artículo, escribe igual en los comentarios o dímelo por Twitter.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.