Component-Store, un vistazo al hermano pequeño de NgRx

A la par que la versión 10 de NgRx apareció junto al framework una librería con el nombre de component-store. Component-store es una librería que nos facilita la gestión del estado de los componentes, mediante la instanciación de un servicio que expone un ViewModel y medios para interactuar con él, de forma muy similar a un desarrollo en el que tenemos servicios singleton que exponen observables mediante objetos Subjects para intercambiar información entre distintos componentes.

Si nos centramos en NgRx en particular, component-store es una alternativa a la gestión del estado mediante un Store Global, que es el mecanismo con el que hemos estado trabajando hasta ahora en una aplicación Angular.

Hasta ahora, la gestión del estado mediante NgRx se centralizaba en un único objeto a nivel de aplicación (formado por uno o varios reducers y dividido a lo largo de varios módulos) que contenía el estado global de la misma. Es un estado que se inicia y muere con el ciclo de vida de la aplicación. Esta nueva propuesta limita la vida del estado de un componente al ciclo de vida de un componente, por lo que el estado se inicializara en el momento de la creación del Componente y se eliminara al destruirse el componente.

El uso de ambos estados no es excluyente y ambos gestores de estados son complementarios, es decir nuestra aplicación puede tener un estado global a nivel de aplicación y usar component-store alli donde lo necesitemos. La pregunta tal vez seria cuando necesitaríamos uno u otro.

A nivel de Arquitectura un gestor de estado no es un solo un objeto que contiene el estado actual de la aplicación, no es solo el sitio centralizado donde se producen los cambios del estado, sino que también es un orquestador de información y de peticiones de cambios de estado, y por ende del comportamiento de la aplicación en función de su estado.

Dame un ejemplo de uno y otro….

  • Global Store: Imaginemos la situación en la que un cambio del estado debiera provocar cambios en varias áreas / zonas de la app, por ejemplo, el usuario interactúa con la cabecera y cambia el idioma de la aplicación. Otro, al recibir una respuesta de una API, esa Action tuviera que provocar el cambio en dos estados definidos cada uno en un módulo (ngModule). Estos son ejemplos claros que al menos nos dan una pista de la necesidad de la creación de un estado global mediante NgRx.
  • Local State: Por otro lado, podemos encontramos en la situación de una vista con un grid y un filtro, datos y propiedades de estado que no son transcendentales para el resto de la aplicación. Estado que no hay necesidad que tenga vida más allá del momento en el que el usuario está en la página (o el componente exista). Ante estas situaciones similares tal vez la solución pasaría por el uso del Component-Store como gestor del estado.

¿Tiene algun coste añadido esta nueva alternativa?

Si bien todos los escenarios que se han planteado son solventables mediante el uso de Store Global, ¿cuál es la necesidad de plantearnos otra alternativa? La respuesta es el coste de desarrollo y el boilerplate que plantea la solución tradicional en contra de la simplicidad de la idea de un component-store.

Nótese en la siguiente imagen un ejemplo de los elementos necesarios a nivel de modulo para gestionar el estado mediante un Store tradicional.

Por otro lado, en la decisión de un component-store normalmente tendremos un nuevo servicio asociado a un componente en el cual tendremos tanto la definición del estado como los métodos necesarios para leer y provocar cambios sobre él.

Tengamos en cuenta que ambas soluciones añaden complejidad al desarrollo y tenemos que medir y saber si necesitamos un gestor del estado. Esta complejidad va asociada a un nivel de indirección en el código (y los datos) que al principio nos puede costar ver un poco, si bien es verdad que una vez asimilado, rápidamente lo adoptaremos como solución.

A nivel de código / arquitectura ¿qué diferencias hay?

Ambas soluciones estan basadas en una arquitectura Push mediante observables, en la que de una manera u otra nosotros solicitaremos cambios del estado, pero ese nuevo valor no será devuelto en la ejecución de la acción, sino que tendremos que estar subscritos a unas propiedades que emitirán eventos antes cualquier cambio.

Como mencionamos anteriormente la mayor diferencia es el numero de objetos involucrados en cada una de las soluciones, pero ojo no por tener menos elementos es mejor ni por tener más es peor, o viceversa. Ambas opciones estan orientadas a situaciones distintas y esta nueva viene a complementar la solución tradicional.  Planteemos el Global Store como solución para mantener el estado común de la aplicación, accesible desde distintos módulos o componentes. Es verdad que ante esta situación el uso de módulos con Store propio puede disminuir ya que un simple component-store declarado como servicio a nivel de modulo puede sustituir el Store a nivel de modulo, ya que la instancia como singleton será compartida por todos los componentes que la requieran . Pero como digo es complementario y hay que analizar muy bien donde podemos hacerlo.

Una gran diferencia con respecto al Global Store es relativa a la forma en la que se producen los cambios de estado.

  • Global Store: Al hacer dispatch de un Action sobre un objeto Store, esta puede ser manejada por varios effects o reducers, lo que se traduce en que una única acción pude modificar distintas partes de estado (y en distintos módulos). Esto es muy poderoso.
  • component-store: No existe como tal el concepto de Action, sino que se realiza mediante llamadas a métodos updaters y effects del servicio ComponentStore. Esto no elimina la posibilidad de que la lógica incluida en los effects pueda lanzar una acción sobre un Store Global.

Otra diferencia es la no integración con las DevTools. En component-store al no existir el concepto de Action y al estar localizado el estado a nivel de Componente no existe tal integración. Todo aquellos que estamos acostumbrados a trabjar con Redux y NgRx en Angular, sabemos el potencial del uso de esta herramienta del navegador y de primeras resulta incluso un poco incómodo. Para ayudar en el desarrollo y tener una trazabilidad de los cambios de estado en los componentes, podemos implementar una extensión de la clase ComponentStore para inyectar un log en la consola muy similar al que usamos con las DevTools.

En resumidas cuentas, esta nueva solución propuesta por el equipo de NgRx me ha dado la posibilidad de utilizar un enfoque parecido al que propone otro gestor de estado como Akita pero sin renunciar a la potencia de los Effects ni a un framework tan potente como es NgRx.