Architect v0.3.0
Novedades en la versión
Siguiendo con la iniciativa de #BuildingInPublic os dejo un pequeño artículo con mi experiencia de implementar el sistema de importación/exportación para las aventuras.
Estos días he tenido la mayor crisis (aunque también victoria) arquitectónica hasta el momento, desarrollando el motor de Architect.
Si estás familiarizado con los SaaS o manejando estados complejos en React, seguro que esto te va a sonar familiar:
El Dios omnipresente del Singleton
Cuando construyes un editor complejo en el navegador (como es el caso de este motor de narrativa y su herramienta de diseño), la forma en que gestionas la memoria dicta el límite de lo que tu aplicación puede hacer.
Desde el principio, siempre imaginé Architect como un fabuloso Dashboard en su backend, desde el que poder gestionar todas las aventuras que fuera creando. Cada una de esas aventuras, debería incorporar su propio editor, desde el que diseñar los nodos, los objetos y las lógicas propias de cada historia.
Todo parecía que iba viento en popa hasta que creé una segunda aventura y empecé a configurarla. Vi entonces que esta compartía información con la anterior. Aquí había un grandísimo ERROR, la IA había decidido no respetar uno de los principios más importantes en la programación orientada a objetos, el principio de encapsulamiento.
¿Qué había pasado? Bueno, no seremos malos con la IA, ya que todo ha tenido una explicación racional. La IA había diseñado mis gestores de estado como un Singleton global. Para entendernos, es como si toda la memoria de la aplicación estuviera compartiendo un mismo cajón donde guardarse. No importaba en qué aventura estuvieras, hicieras lo que hicieras editabas el tejido de todo el multiverso a la vez.
¿Por qué hizo esto la IA? Por simplicidad y dado que estábamos prototipando desde cero. En esa primera fase de la aplicación, la IA pensó que era buena idea gestionar el estado global con un patrón de Singleton persistido. Claro, era rápido de implementar y daba cero problemas con bases de datos, pero eso escondía una bomba de relojería. Solo era cuestión de tiempo que dejara de ser funcional.
¿Cómo se ha solucionado?
Con una arquitectura Multi-Tenant y el concepto de “Cartucho”. Así que ha tocado hacer lo que ningún programador quiere hacer REFACTORIZAR (Ay mamá…😧 )
Pero en fin, como en este caso yo hacía un poco de jefe, pues ya le echaría las culpas a la IA si algo no se hacía como debía, jeje. Entonces manos a la obra: A reestructurar el motor entero hacia un patrón “Multi-Tenant”.
Para lograrlo se le tenía que explicar muy claramente a la IA que iba a dividirse la memoria en dos conceptos:
- La Estantería (El disco duro): Un diccionario que guardaría todas las aventuras bajo un ID único. Cero interacción directa con la interfaz de edición.
- El Escritorio (La memoria RAM): Cuando haces clic en “Abrir Editor”, el motor coge los datos de ese ID de la estantería, los hidrata en el escritorio y trabajas solo ahí. Lo que pasa en esa aventura, se queda en esa aventura (Como en Las Vegas)
Sería el equivalente a meter un cartucho de la Super Nintendo (o mejor aún, vayamos más atrás: un cartucho del MSX). El motor es la consola (o el MSX!), pero la aventura está en el cartucho, en un ecosistema estanco.
Tras formular unas cuantas peticiones, finalmente se consiguió. Y por fin, puedo decir que ahora si que tengo una buena base sobre la que construir un sistema de Backup para las aventuras! yeah!
Eso me llevó a la siguiente duda,
¿XML o JSON?
Implementé un sistema de Exportación/Importación directa desde una sección que pasé a llamar “Gestión del cartucho” (Maldita nostalgia, ¡no puedo evitarlo!). Y la elección del formato JSON se impuso por encima de XML. ¿Por qué?
- Es universal: Si si… lo sé. XML también lo es. No se trata de una ventaja de JSON sobre XML, pero si de una ventaja de SGML sobre el formato binario. Así que no puedo obviar su importancia. Para quien no lo sepa, guardar los datos en JSON dota a la información de independencia respecto a la aplicación sobre la que trabaja. Si el día de mañana mi app desaparece, podré seguir teniendo las aventuras guardadas, con todos sus textos, sus lógicas y sus rutas en un formato legible por humanos e importable a cualquier otro futuro motor de aventuras. En definitiva, les da persistencia a futuro.
- Es ligero y menos verboso: XML es muy pesado porque necesita abrir y cerrar una gran cantidad de etiquetas. JSON, por su parte, es sumamente minimalista. Una aventura podría tener fácilmente varios cientos de nodos, varios tipos de personajes y unas cuantas decenas de objetos. Para un juego así, el formato JSON ahorra gran cantidad de peso.
En el papel todo suena idílico, pero hacer un #BuildingInPublic también es contar las bofetadas que te llevas. Así que, aquí va una lista de lo que no tuve en cuenta:
1. Los “Datos Fantasma” (Clean Slate): Al crear el aislamiento, cada aventura nueva nacía con “datos de prueba” (nodos default, ítems que no existían, etc…). En definitiva, una cantidad de datos hardcodeados que daba miedo. Tuve que crear una directiva estricta de “Lienzo en Blanco”. Ahora, al sembrar una semilla, se fuerzan arrays estrictamente vacíos. Si el creador no lo ha escrito, el motor no se lo inventa.
2. La guerra contra el Iframe: La IA implementó un botón para borrar aventuras (Obviamente, ¿no solo iba a crearlas no?). Le puse el clásico y rápido window.confirm(“¿Seguro que quieres borrar?”) de JavaScript. Pero… sorpresa: los entornos modernos de previsualización bloquean por seguridad los popups nativos del navegador. Así que el resultado era que el botón no hacía nada.
¿La solución? Decirle diseñara un sistema de doble confirmación en línea en React puro. Ahora hay un iconito super mono de una papelera que cuando lo clicas se transforma en un botón rojo de “Confirmar Borrado” durante 3 segundos. Si clicas de nuevo, usa inmutabilidad de estado para hacer desaparecer la tarjeta visualmente al milisegundo. Mucho más elegante, cero bloqueos, y mejor UX. Yeah! 😁
Próximo milestone
Después de esta odisea, los cimientos del motor ahora están bien asentados. Tengo estanqueidad total entre proyectos y una herramienta de backup funcional. He pasado de un prototipo chulo a una arquitectura de producción.
Ahora que la casa ya no se va a caer… toca llenarla de muebles.
Y aquí os pregunto a los que seguís el proyecto:
Para la siguiente fase, ¿qué sistema os gustaría ver?
¿Implementar herramientas de “Quality of Life” (QoL) para el creador de nodos?
…como un minimapa, selección múltiple para mover grupos de nodos a la vez, agrupación por zonas/cajas de color, o un buscador para encontrar rápidamente un nodo por su ID o título.
¿O le damos vueltas a un sistema de guardado? QoL para los jugadores. Para que no haya que volver muy atrás si te matan… (esto tendrá debate… va de game design)
Un saludo!
Felip Granados

