Arquitectura de Componentes en Angular
Tags: angular, components, architecture, statemanagement
Published: 2021-06-08 00:58:57

Nuestras aplicaciones estan llenas de componentes y pensar en cómo queremos estructurarlos, nos ayuda en su arquitectura.


La mayoría de aplicaciones que construimos no son páginas estáticas, hay un estado y hay diferentes tipos de componentes donde el estado podría vivir.‌


# Tipos de Componentes

![Tipos de componentes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ao9c2mhs7y1lo99rggif.png)


## Presentational componentes o Componentes de presentación


Muy conocidos como Dumb o componentes tontos. Usan Inputs/Outputs para su comunicación. 


Enviamos un valor al container, renderizamos los valores del container; emite un evento cuando está listo y algún otro componente administra el estado y hace las actualizaciones respectivas del estado.


Con estos componentes nos probamos a nosotros mismos en cuanto a la separación de la complejidad en la lógica que podemos llegar hacer, en estos componentes no hay necesidad de crear servicios mockeados, o hacer alguna petición solo pasamos los inputs, se verifica alguna información de ser necesaria y se emite un output.


## Container components o Componentes contenedores

Estos componentes son los Smart o inteligentes. Se encargan de conectar los "Dumb" al estado de la aplicación. Maneja los eventos emitidos, el de presentación emite un evento, y este responde a ese evento y se hace los llamados al servidor y actualiza lo necesario para ver los cambios necesarios en la UI o interfaz de usuario. El contenedor es el que controla otros componentes, es como un administrador, se encarga interactuar con los servicios o el **state Management**.


## Layout components o componentes de diseño

Para estos no se maneja data, por lo que no es necesario una actualización cuando hay un cambio. Lo podemos ver como una página que tiene una estructura definida, o una plantilla que nos servirá para luego organizar otros componentes.

## P‌age components o componentes de página

Los componentes de página funcionan como componentes contenedores, estos son los componentes que serán usados en el enrutamiento de nuestra aplicación.

# Flujo de Datos

Cuando estamos creando nuestra aplicación podemos usar los tipos de componentes de la siguiente forma:‌


![Flujo de Datos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e0dfkq5duh4el5u5o7z3.png)


Entre el contenedor y el componente de presentación, una forma de compartir la data es mediante **Input** y **Output**.

![Input/Output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhyc0xfvl7rojbc0rmy4.png)


Un contenedor puede ser un componente padre, pero también un componente presentacional podría serlo, sin modificar el estado y solo compartiendo la información con sus hijos que viene desde un padre contenedor.


![Componente Padre -> Componente Hijo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gccyi5j8mu44bcgk54if.png)


El **Input** nos permite pasar una data como entrada a nuestro componente, comúnmente la usamos para pasar data entre padres a hijos, si necesitáramos pasar desde el hijo al padre, usamos el **Output**.


# Otros Flujos de Datos

Adicional al flujo de datos que es comúnmente usado, se tienen otros métodos que nos ayudan con el paso de la data entre padres e hijos. Del curso de [Arquitectura de **Dan Wahlin**](https://app.pluralsight.com/library/courses/angular-architecture-best-practices/table-of-contents) , tome las siguientes notas que te pueden ser muy útiles en la estructuración de tus componentes: 

La data viene de una petición como por el **HttpClient**, una Store (**NgRx**), o una ruta y todo va al contenedor, esas diferentes fuentes van al contenedor y este hace lo que requiere el estado y lo pasa al componente presentacional, para renderizar lo que se necesita.


![Otros flujos de Datos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pfxzua58zba1bdj0e8et.png)

Son dos componentes importantes: uno muestra el contenido y el otro maneja la data.


## Estrategias de Change Detection


**OnPush** causa que el "**Change Detection**" se ejecute cuando:‌

* Una propiedad de **Input** cambia de referencia.
* Una propiedad de **Output**/**EventEmitter** o **DOM** dispara un evento.
* **Async Pipes** reciben un evento.
* **Change Detection** es manualmente invocado por el **ChangeDetectorRef**.


### Beneficios del OnPush


* Optimización (los componentes no están verificados hasta que se cumpla una condición del **OnPush**)
* Prevenir que los componentes de presentación actualicen el estado que deberían obtener del contenedor/padre


## Otra forma de comunicar componentes


Con el **Input** y **Output** tenemos una buena comunicación entre los componentes, pero cuando incrementa la complejidad de nuestra aplicación y se necesita una mayor jerarquía, se puede volver algo complejo usar esta forma conocida y es necesario usar otras técnicas de comunicación.


### Event Bus


Es un patrón mediador, el servicio actúa como un intermediario entre los componentes. Los componentes no saben de donde viene la data y es débilmente acoplado. Se basa en **Subject**/**Observable**.

![Event bus](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pghfrdmr8jzu2gn6ziu2.png)


### Observable service

Proviene del patrón **Observer**. El servicio expone un observable directamente a los componentes. Los componentes saben de donde proviene la data, no es débilmente acoplado como el **Event bus**. Se basa en **Subjects**/**Observable**.

![Observable Service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/05l9muhd9p7lqv9t2jrb.png)

### RxJS

Es una librería de programación reactiva, basada en eventos a través de secuencias de observables.


#### RxJS Subjects


* **Subject**. Los **subject** proporcionan una manera de enviar uno a más datos a los oyentes. Estos oyentes están suscritos y obtienen la información. En el caso de haber nuevos suscriptores estos no obtendrán la data que se pasó antes a los oyentes, sólo los nuevos obtienen la data que se está transmitiendo desde el momento de la suscripción.

![Subject](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bvo49i8yv39pcj8sk95g.png)


* **BehaviorSubject**. Es muy similar a los **subject**, con la diferencia que los nuevos suscriptores pueden obtener la última información que se haya pasado previamente a su suscripción.

![Behavior Subject](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f1sa0kafgxjm4cmhipoe.png)

* **ReplaySubject**. Este es una especia de **BehaviorSubject**, este puede repetir el último valor que haya sido pasado al momento de la suscripción e incluso se puede configurar si se desea pasar los valores anteriores.

![Replay Subject](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7qyd1496zsadp3ubxxru.png)

* **AsyncSubject**. Este es diferente a los demás, con él se pasa el valor más actualizado.

![Async Subject](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p09dxhziur3fwlgj8onj.png)



## ¿Por qué necesitamos un estado?


Se tiene un servidor que tiene un valor, se puede tener las rutas que trae data que se comparte entre ellas, y se muestra esa data en la página. Necesitamos la data proveniente de algún lugar para mostrarla en otro; y el estado es quién se encarga de ayudarnos en la comunicación de esas dos necesidades, se puede decir que es la interfaz entre los datos y los componentes. Además nos ayuda a tener los datos consistentes entre componentes y a mantener la comunicación entre ellos.‌


## Tipos de Estados


### Estado del servidor

El estado del servidor es la data en el Backend. Hacemos peticiones de la data al servidor, mediante puede ser un **HttpClient** o una url y hacemos la petición de la data.


### Estado de la aplicación

El estado de la aplicación es lo que nos ayuda a persistir la data a través de toda la aplicación.


### Estado de la página

El estado de la página es lo que solo es necesario en la página.‌

En este punto llega un gran interrogante, ¿cómo saber cuál debemos usar o cómo debemos poner la data?‌

Y debemos hacer una análisis del diseño de nuestra aplicación en este punto nos preguntamos, ¿debo compartir la data en toda la aplicación?, ¿la necesito en ciertas páginas?, ¿cuánto debe durar la persistencia de la data? ¿la comparto en una sesión o debe ser en múltiples?‌

Tenemos varias opciones para responder a nuestras preguntas anteriores:‌

* **Servicios**. usando el patrón **singleton**, podemos crear un **subject**, exponemos un **observable**, donde se pueden suscribir a él, puedo obtener lo que necesito y si necesito hacer un update, llamo a un set para que se encargue de la actualización. Todos los que se hayan suscrito obtendrán el estado correspondiente, este método ayuda a mantener una consistencia.
* Librerías para manejar el estado (**NGRX**, **NGXS**).
* **Router** o **Enrutamiento**. Almacena la persistencia de la data, permitiendo que exista en una sesión y también permite compartir páginas o rutas. En el enrutamiento podemos compartir parámetros que usaremos a través de la aplicación.
* Estado del componente. Smart component se encarga de manejar todo el estado.


## State Management

Dependiendo del desarrollo de nuestra aplicación, podemos optar por esta opción de necesitar un **State Management**. Los objetivos de este son:‌

* Una sola fuente de verdad.
* Predecible.
* Inmutable.
* Seguimiento a los cambios‌.

Para manejar el estado existen varios opciones:

### Servicios

Es un buen punto de entrada para manejar el estado de la aplicación. Desde la documentación oficial un servicio es un **singleton**, con una única responsabilidad, bien definida. Puede ayudarnos para la comunicación del flujo de datos de una forma sencilla. 

Los podemos incluir mediante la inyección de dependencias en donde los necesitemos y podemos proveer notificaciones usando los **Subjects**, es fácil de mantener y realizar una simple petición al servidor. 

Pero cuando la aplicación empieza a crecer y ser un poco más robusta, podemos continuar usando los servicios, pero es necesario centralizar la información en un **Store**. Los servicios se comunican con la **Store** y de esta forma se garantiza la comunicación con el estado.

![Servicios — Imagen tomada del curso de Dan Wahlin — Arquitectura en Angular](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/au6dtsuxqxeh59xn1919.png)


### NgRx Store

Provee un manejo del estado reactivo para las aplicaciones de Angular, inspirado por **Redux**. Unifica los eventos en la aplicación y dirige el estado usando **RxJS**.

**Redux** + **RxJS** = **NgRx**

* Provee una sola fuente de verdad para el estado.‌
* Provee datos inmutables, los datos no van a mutar o cambiar en múltiples lugares a través de la aplicación.‌
* Provee una consistencia a través del equipo de desarrolladores y podemos tener una buena experiencia en el debugging de la app.‌

En **NgRX** tendremos una **Store**, que será responsable por el almacenamiento del estado de la aplicación, puede ser cualquier tipo de estado (Estado de la aplicación, estado de la sesión, o estado de la entidad).‌

Para interactuar con el **Store** y el estado se tienen **Acciones**, las acciones se pasan a los **Reducers**, que son básicamente traductores que toman las acciones, actúan sobre ellos y luego interactúan con el store del estado. Para obtener esa data en nuestro componente, tenemos los **Selectores** (**Selectors$**).‌

Para obtener la data del servidor, el **componente** llama a una acción, que dispara un **Effect**, que luego se integra al **servidor** y obtiene la data de él. Cuando el **Effect**, obtiene la data de regreso, envía la acción correspondiente (**Action**), va al **Reducer**, el cual actualiza la data del **Store**, y luego el **selector** obtiene esa data de regreso al **Componente**.

![NgRX Store — Imagen tomada del curso de Dan Wahlin - Arquitectura en Angular](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e69liz8c3e0qfdbexg7h.gif)

Usar **NgRx** es muy útil en nuestras aplicaciones, añade un poco más de complejidad y más código, pero sí se define bien el patrón es bueno para el equipo y para el proyecto a futuro.

Tiene una pequeña curva de aprendizaje.


### ngrx-data

Es una extension de la funcionalidad de **NgRX**. Ofrece una buena introducción al uso de **ngrx**/**redux** sin el boilerplate.
 
Podemos decir que simplifica el **NgRx**.

![ngrx-data — Imagen tomada del curso de Dan Wahlin — Arquitectura en Angular](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nneytzkwuqr9stnxnikb.gif)


### Observable Store

Provee una forma de administrar el estado de nuestra aplicación, mientras se logran muchos de los objetivos que ofrecen otros state management complejos.

Entre los objetivos de este **Observable Store**, está el ser una sola fuente de verdad, el estado es solo de lectura e inmutable, provee notificaciones del cambio de estado a los suscriptores; también tiene la posibilidad de hacer un seguimiento al historial del cambio de estado y por último una mínima cantidad de código es requerida.

Trabaja con cualquier librería.

![Observable Store — Imagen tomada del curso de Dan Wahlin — Arquitectura en Angular](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s99aopmoikul0ij0gon5.png)

### Otras Herramientas para manejar el estado: 

* **Akita**.
* **Ngxs**.
* **Mobx**

Entre otras.


# Referencia

[Angular Architecture and Best Practices **Dan Wahlin**](https://app.pluralsight.com/library/courses/angular-architecture-best-practices/table-of-contents)

# Conclusiones

Pensar en la mejor forma de organizar nuestros componentes es una tarea muy importante, hay situaciones o requerimientos que nos llevan a realizar una análisis mas exhaustivo sobre la necesidad de manejar el estado o flujo de datos de nuestras aplicaciones.

En Angular tenemos un gran abanico de opciones y es bueno analizar las ventajas de cada uno de ellos antes de proceder con su implementación. Espero en este articulo en el cual se menciona de forma breve algunas formas que tenemos a nuestra disposición para el flujo de datos y manejo de estados encuentres una ayuda para implementarlos en tus proyectos.

Te invito a seguirme en twitter: [@vanessamarely](https://twitter.com/vanessamarely)