Porque (todavía) no tenemos GraphQL
En un futuro idílico, utilizaremos simplemente algo como GraphQL para componer dinámicamente las solicitudes de datos al servidor, recuperando ni más ni menos de lo que necesitemos. Pero ese día aún no ha llegado.
Mientras tanto, tenemos que solicitar recursos a través de la misma API que utilizan nuestros clientes, lo que a veces incluye datos complementarios y otras veces no.
Tomemos, por ejemplo, nuestro punto final «Job», tal y como se representa en Thrift:
De hecho, una solicitud desde la API para obtener datos de trabajos incluirá matrices de usuarios y documentos de los trabajos, y solo hará referencia a las plantillas de trabajo mediante claves externas. Dejando a un lado el contexto empresarial de un trabajo, puedes imaginarte una sencilla lista de estos trabajos en una barra lateral, cuya selección pasa a ser el elemento central del panel principal.
Primera iteración: React y el paso de props
Cuando hicimos nuestro primer intento de crear una interfaz de usuario basada en este concepto, dejamos que React se encargara de gran parte de la gestión de datos, pasando los datos —a veces una cantidad considerable— a los componentes hijos en forma de props.
Sigamos con el ejemplo de una lista de trabajos (barra lateral izquierda, arriba). He aquí un primer intento bastante aceptable:
Los aspectos clave que hay que tener en cuenta son:
- El componente principal recibe como props el trabajo seleccionado actualmente, una lista de todos los trabajos y un «idObjectCache», que no es más que un almacén de datos de consulta que se actualiza manualmente.
- El padre transmite al hijo toda la misma información
- Manipulación de datos mediante una cadena de funciones de Underscore dentro de `getRows ` (Perdón, quería decir Lodash, claro. Lo siento, a todos).
- Una pequeña y tonta función de búsqueda en el elemento secundario llamada «templateIdFromName»
Se trata de un ejemplo relativamente sencillo, pero la situación se complica rápidamente en las áreas más complejas de la aplicación, donde tenemos que combinar fuentes de datos más dispares.
Además, resulta que, oculto en el modelo «job», se encuentra el modelo «team» completo, que utilizamos en el código anterior (this.props.job.team.name), pero que nunca abstraemos para otros componentes. En la misma vista de página también estamos realizando una llamada a la API para obtener el equipo del trabajo actual.
Segunda iteración: obtención de datos de claves externas en NuclearJS
No es ningún secreto. Me encanta a más no poder NuclearJS. Aquí tienes un primer borrador para dejar de lado Alt y pasarme a mi nuevo flechazo:
Las diferencias son sutiles, y es posible que su importancia no resulte evidente si [todavía] no has utilizado React en situaciones reales.
- El contenedor principal ahora utiliza los getters de Nuclear para recuperar listas inmutables de datos, con todos los procesos de filtrado y ordenación ya gestionados.
- Los únicos parámetros que recibe el componente están relacionados con el estado de la interfaz de usuario (por ejemplo, «cargando ofertas de empleo»), pero gestiona internamente la recuperación de sus propios datos, lo que significa que tanto el componente como su padre pueden refactorizarse o reubicarse con total seguridad.
- El módulo «Job» es un módulo común que utilizamos en dos aplicaciones diferentes, pero «currentJobId» es específico de la aplicación WebEx (ejecución web —disculpad la jerga interna—) y, por lo tanto, de su propio módulo.
- Nos conformamos con el método getIn() , mucho más seguro, para la recuperación profunda.
El resultado es mucho más conciso y mucho más fácil de entender, aplicar estilos y adaptar para nuestros diseñadores. Genial, pero ¿qué hay de la obtención de datos?
Gestiona los datos de claves externas como lo haría Jason Bourne
La documentación de Nuclear ofrece una estrategia excelente para crear acciones de API modelo por modelo. Dado que el envoltorio que hemos escrito en torno a Thrift devuelve Promises, muchas de nuestras implementaciones de modelos son simples asignaciones:
exports.fetchAll = Thrift.enhanced.JobRole.index;
Pero en los casos en los que queramos gestionar los datos de clave externa de lo que se devuelve, podemos acabar rápidamente con nuestros enemigos como lo haría Jason Bourne, de la siguiente manera:
Nota: Hay algunos puntos en el código anterior en los que podríamos ser más concisos, pero al menos queda muy claro para el próximo ingeniero (¡eso espero!) lo que está pasando.
Interceptar la respuesta de la API
El primer truco consiste en que, dependiendo de los parámetros enviados por la interfaz de usuario (es decir, un componente de React), la acción utiliza diferentes puntos finales de la API. Dado que tanto la función de Thrift como la de Nuclear devuelven Promises, podemos intervenir en medio y pasar la matriz de tareas resultante a `processForeignKeys`.
Solicitud de datos de claves foráneas obligatorias
El segundo truco consiste en obtener una lista única de identificadores de plantillas de trabajo a partir de la lista de trabajos (para más de 50 trabajos, es posible que solo haya entre 3 y 4 plantillas). Una llamada al índice de plantillas tendría que paginarse, por lo que, en lugar de recuperar todas, podemos enviar solicitudes solo para lo que necesitamos. Si el punto final del índice llegara a admitir la recepción de una matriz de identificadores, podríamos enviarla en una sola llamada.
Por supuesto, estas acciones son del tipo «fire-and-forget». De forma gratuita, nuestros getter actualizarán los componentes de React cuando lleguen los datos de la clave externa. ¡Es lo más!
Carga de datos de claves externas incrustadas
El tercer truco consiste en que podemos tomar los datos de los usuarios, los puestos de trabajo y los equipos de todos los trabajos y cargarlos mediante una sencilla función «receiveAll» que hemos creado:
En cada modelo, la función `receiveAll` simplemente simula que está gestionando una solicitud de obtención de datos de la API que ella misma ha iniciado. Los resultados son los mismos.
Quizá también hayas observado más arriba que, al procesar datos duplicados de claves externas en diferentes tareas, modificamos la función `uniq()` para limitarla a comparar simplemente los ID. Esto resulta mucho más sencillo que realizar una comparación exhaustiva, aunque se basa en nuestra confianza en que el servidor no nos enviará datos diferentes con los mismos ID en una misma solicitud. Si no podemos contar con ello, tendremos problemas más graves.
La última etapa: los «getters»
Has llegado hasta aquí. Vamos a meter el balón en la zona de anotación.
El formato de los «getters» de Nuclear parece un poco recargado a primera vista, pero es genial.
- The raw job data itself is stored as an Immutable Map of String<jobId> to Map<job>
- La clase `jobList` supervisa los datos de los trabajos y las plantillas de trabajo, realiza búsquedas a medida que las plantillas están disponibles y devuelve los resultados en forma de lista inmutable.
- Las exportaciones `activeJobList` y `completedJobList` simplemente aplican filtros y ordenaciones, tal y como se haría en cualquier lenguaje o marco de trabajo. Simplemente obtienes un enlace de datos unidireccional sin ningún coste adicional.
Al final, tenemos…
- Todo nuestro código de la API en un envoltorio Thrift
- Todos nuestros datos de la API se almacenan como objetos inmutables, gestionados de principio a fin por Nuclear, incluidas las dependencias de datos de claves externas.
- Toda nuestra interfaz de usuario está desarrollada en React, con la recuperación y el enlace de datos localizados en la medida de lo posible.
…lo cual es tan fácil que me deja tiempo libre para escribir una entrada en Medium sobre todo esto.
Si te ha resultado útil, no dudes en recomendarlo.
Ah, sí, y estamos buscando a nuestro tercer ingeniero de front-end, por si te apetece que te paguen por darlo todo con React, Nuclear e ImmutableJS en una startup financiada en la ronda de Serie A y respaldada por Andreessen Horowitz, First Round, Kleiner Perkins y Google Ventures, entre otros. Escríbeme.











