Skip to main content

Necesidades de negocio (historias de usuario, diseño, etc)

Necesidades de negocio (requerimientos, historias de usuario, criterios de aceptación, diseño, UX/UI, análisis de impacto esfuerzo de features, refinamiento, pruebas de usuario, focus group, brainstorming, etc)

Arquitectura base (capas)

📦 Features

  • Dividir la app en módulos funcionales (Feature Modules) en lugar de tener todo al mismo nivel que la carpeta app.
  • core para manejar servicios singleton y configuraciones globales.
  • shared para componentes, pipes y directivas reutilizables.

Los módulos deberan tener la siguiente estuctrura de carpetas:

  • Carpeta models donde estaran las interfaces y tipos (Model).
  • Carpeta services donde estaran los servicios que gestionan la data y se comunican con la API.
  • Carpeta viewmodels donde estaran los componentes inteligentes que manejan la lógica de negocio (ViewModel).
  • Carpeta views donde estaran los componentes "tontos" que solo muestran datos (View)
  • Archivo de rutas, Ej: products.routes.ts.

│── features/ # Funcionalidades organizadas
│ ├── dashboard/ # Módulo de negocio específico.
│ ├── services/ # Servicios propios del modulo
│ ├── model/ # Modelos de datos.
│ ├── viewmodels/ # Adaptadores entre modelos y vistas (comp "inteligentes")
│ ├── views/ # Componentes visuales del modulo. (comp "tontos")

Lineamientos:

AtributoDefinición
NombreEl nombre de la carpeta del modulo se debe declarar lo más sencillo, descriptivo y corto posible.
IdiomaInglés
Estilo Variablekebab-case
Nomenclatura<module-name> Ejemplo: products
Consideraciones- No usar espacios, tildes, caracteres especiales ni la letra ñ. Evitar el uso de abreviaturas o acrónimos. Evitar combinar palabras inglés-español.
danger

Nota: Ocupar el patrón standalone esta prohibido usar los archivos <name>.module.ts y <name>-rounting.module.ts


⚙️ Services

Los servicios en microfrontends (MFEs) en Angular deben ser diseñados para garantizar independencia, comunicación eficiente y escalabilidad. Por lo anterior, se deberán validar y seguir las siguientes prácticas para implementar servicios correctamente en una arquitectura basada en microfrontends.

1. Independencia de los servicios

  • Cada microfrontend debe tener sus propios servicios para garantizar que los módulos sean autónomos.
  • Los servicios no deben depender de otros microfrontends para evitar acoplamiento entre ellos.
  • Utiliza inyección de dependencias para que los servicios sean fácilmente testeables y reutilizables.

2. Comunicación entre microfrontends

  • Eventos Globales: Utiliza un bus de eventos (por ejemplo, RxJS) para permitir que los MFEs se comuniquen entre sí sin acoplarse directamente.
  • Interfaz de Comunicación: Define un contrato claro y API estándar para la comunicación entre microfrontends. Asegúrate de que la comunicación sea robusta y versionada.

3. Escalabilidad

  • A medida que crecen los MFEs, los servicios deben ser capaces de manejar más usuarios y más peticiones de manera eficiente.

4. Testing

  • Asegúrate de que los servicios sean probados de forma aislada y sin depender de otros microfrontends.
  • Utiliza pruebas unitarias, pruebas de integración y pruebas end-to-end (E2E) para asegurar que los servicios se comporten como se espera.

5. Documentación

  • Documenta las APIs y servicios de manera clara y accesible para todos los equipos de desarrollo. Esto facilitará la integración y escalabilidad de los microfrontends.

Lineamientos:

AtributosDefinición
NombreSe debe declarar el nombre lo más sencillo, descriptivo y corto posible.
DefiniciónParte fundamental en el desarrollo de un componente que será usada para comunicar el aplicativo front con las APIs.
IdiomaInglés
Estilo Variablekebab-case
Nomenclatura<name>-service.ts
Ejemplogeneral.service.ts
ConsideracionesNo usar espacios, tildes, caracteres especiales, ni la letra ñ. Evitar el uso de abreviaturas o acrón
tip

Seguir las siguientes recomendaciones a la hora construir un servicio:

  1. Se recomienda usar Observables en los retornos de las respuestas de los servicios.
  2. Se recomienda usar Interfaes en las respuestas de los servicios.
  3. Se recomienda hacer operaciones y transformaciones de datos con rxjs antes de pasar la información a las vistas.

Ejemplo:

import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { map, Observable } from "rxjs";
import { IProductCard } from "../models/product.model";
import { IResponseAnime } from "../models/anime-api.interface";

@Injectable({ providedIn: "root" })
export class ProductsService {
constructor(private _httpClient: HttpClient) {}

getProducts(): Observable<IProductCard[]> {
return this._httpClient
.get<IResponseAnime>("https://api.jikan.moe/v4/anime?q=kimetsu&sfw")
.pipe(
map((response) => {
return response.data
.filter((item) => item.synopsis)
.map<IProductCard>((item) => ({
urlImage: item.images.jpg.image_url,
price: this._getNumberRandom(),
name: item.title,
description: item.synopsis!,
}));
})
);
}

private _getNumberRandom() {
return Math.floor(Math.random() * (500 - 1 + 1) + 1);
}
}

Cada microfrontend debe manejar su propio estado de servicio y evitar compartir instancias globales.

Comunicación entre Microfrontends

Cuando los microfrontends necesitan comunicarse los únicos mecanismos disponibles para ello será:

  • Eventos
  • Event Bus con RxJS

🧠 ViewModel Web

Los archivos "Viewmodel" son componentes que contienen la capa de lógica, cuya principal función es:

  • Obtener y procesar datos.
  • Manejar eventos.
  • Formatear fechas, números, etc.
  • Transformar datos para gráficos o listas
  • Comunicar la vista con el modelo.

Partiendo de este enfoque, los archivos .component.ts y .component.html se convierten en componentes "tontos" que solo muestran datos y se comunican exclusivamente con el Viewmodel.

tip

Beneficios de usar ViewModels

  1. Separación de responsabilidades
  2. Reutilización de lógica de transformación
  3. Evita lógica innecesaria en el componente

Lineamientos:

AtributosDefinición
NombreSe debe declarar el nombre lo más sencillo, descriptivo y corto posible.
DefiniciónSon componentes inteligentes que se encargan de procesar la información de los servicios y atender las reglas de negocio.
IdiomaInglés
Estilo Variablekebab-case
Nomenclatura<name>-viewmodel.ts
Ejemploproducts.viewmodel.ts
ConsideracionesNo usar espacios, tildes, caracteres especiales, ni la letra ñ. Evitar el uso de abreviaturas o acrón

⚙️ Componentes

Los componentes es la parte fundamental de nuestro proyecto, ya que controlan la vista (HTML) y manejan eventos del usuario. También reciben datos desde ViewModels y usan servicios para obtener datos o ejecutar lógica de negocio.

Por lo general nuestros componentes, contendran 2 archivos pricipales, los cuales son:

  • Archivo HTML (vistas)
  • Archivo SCSS (estilos)

A continuación se muestra el flujo de trabajo de los componentes:

Estructura-Modulos

Lineamientos:

AtributoDefinición
NombreSe debe declarar el nombre lo más sencillo, descriptivo y corto posible.
DefiniciónUn componente en Angular es un elemento que está compuesto por:
- Un archivo template: <component-name>.component.html
- Un archivo de lógica: <component-name>.component.ts
- Un archivo de estilos SCSS: <component-name>.component.scss
- Un archivo de pruebas: <component-name>.component.spec.ts
IdiomaInglés
Estilo Variablekebab-case
Nomenclatura<component-name>.component
Ejemplos:
- app.component.html
- my-scheduler.component.ts
Consideraciones- No usar espacios, tildes, caracteres especiales ni la letra ñ.
- Evitar el uso de abreviaturas o acrónimos.
- Evitar combinar palabras inglés-español.

📐 Interfaces

En Angular (y en TypeScript en general), las interfaces son una herramienta fundamental para definir la estructura de los datos que se usan en la aplicación.

export interface IUser {
id: number;
name: string;
email: string;
}

🎯 ¿Para qué sirven?

  • Definen contratos: Permiten establecer claramente cómo debe ser la estructura de un objeto.
  • Facilitan el autocompletado y la documentación en los editores como VSCode.
  • Previenen errores: Ayudan a detectar errores en tiempo de desarrollo si un objeto no cumple con la estructura definida.
  • Mejoran el mantenimiento del código: Hacen que el código sea más legible y fácil de entender para otros desarrolladores.

🛠️ ¿Dónde se usan en Angular?

  • Al definir los modelos de datos que vienen de APIs o se envían a ellas.
  • En los servicios para tipar los datos que se reciben o envían.
  • En componentes para tipar inputs, outputs, formularios, etc.
  • En estructuras complejas como listas de objetos, configuraciones, etc.
getUser(): Observable<IUser> {
return this.http.get<IUser>('https://api.example.com/user');
}

Lineamientos:

AtributoDefinición
NombreSe debe declarar el nombre lo más sencillo, descriptivo y corto posible.
DefiniciónNos ayudan a definir y tipar los datos, lo que mejora la seguridad, mantenibilidad y escalabilidad del código.
IdiomaInglés
Estilo Variable- PascalCase (para nombrar la interface)
- kebab-case (para nombrar el archivo)
Nomenclatura<name>.model.ts
Ejemplos:
- user.model.ts
- export interface <IName> {}
- export interface IUser {}
Consideraciones- No usar espacios, tildes, caracteres especiales ni la letra ñ.
- Evitar el uso de abreviaturas o acrónimos.
- Evitar el uso de combinaciones inglés - español.

📁 Estructura Completa del Proyecto

La organización del proyecto debe reflejar la separación de las capas mencionadas. A continuación, se presenta una estructura de carpetas recomendada:

src/
└── app/
│ │── config/ # Objetos de Valor utilizados en la configuración de la app
│ │── core/ # Servicios centrales y fachadas compartidas por toda la app
│ │── features/ # Funcionalidades organizadas
│ │ ├── dashboard/ # Módulo de negocio específico.
│ │ ├── services/ # Servicios propios del modulo
│ │ ├── model/ # Modelos de datos.
│ │ ├── viewmodels/ # Adaptadores entre modelos y vistas (comp "inteligentes")
│ │ ├── views/ # Componentes visuales del modulo. (comp "tontos")
│ │── pages/ # Componentes de página que orquestan vistas completas.
│ │── shared/ # Componentes, directivas y utilidades reutilizables.
│ │── app.component.ts # Componente raíz de la aplicación.
│ │── app.config.ts # Configuración general de la aplicación.
│ │── app.routes.ts # Definición de rutas principales.
│── assets/ # Archivos estáticos (imágenes, fuentes, etc).
│── environments/ # Configuraciones para distintos entornos.
└── main.ts # Punto de entrada principal de la aplicación.

🚨 Manejo de errores

Para poder manejar los errores Angular usa HttpClient para hacer peticiones HTTP. Se puede manejar errores en los llamados a la API usando el operador catchError de RxJS, que nos permite capturar los errores y realizar alguna acción en consecuencia (como mostrar un mensaje de error o redirigir al usuario).

tip

Se recomienda hacer uso de interceptores, para el manejo de los errores. Y para ello se recomienda las siguientes consideraciones:

  1. El archivo interceptor, debera estar registrado en el host
  2. El archivo interceptor, debera ser expuesto por el host y en los remotes se debera importar

🔤 Nombramiento de métodos

A continuación se muestra los estandares para el nombramiento de métodos que se recomiendan usar:

PrefijoPropósitoEjemplo
getObtener datos o una propiedadgetUser(), getOrderStatus()
setAsignar o modificar datossetUserName(), setThemeColor()
fetchObtener datos de una API o servicio asíncronofetchUsers(), fetchOrders()
loadCargar datos o inicializar informaciónloadDashboard(), loadSettings()
createCrear un nuevo recurso u objetocreateUser(), createTask()
updateModificar un recurso existenteupdateProfile(), updateOrderStatus()
deleteEliminar un recursodeleteUser(), deleteItem()
removeQuitar un elemento de una colecciónremoveFromCart(), removeItem()
resetRestaurar valores a su estado inicialresetForm(), resetPassword()
toggleAlternar un estado entre dos valorestoggleSidebar(), toggleTheme()
handleManejar eventos o acciones del usuariohandleClick(), handleInputChange()
canDeterminar si una acción es permitidacanActivate(), canEditPost()
isVerificar un estado o condición (devuelve boolean)isLoggedIn(), isUserAdmin()
shouldDeterminar si algo debe ocurrir (devuelve boolean)shouldShowModal(), shouldUpdateProfile()
hasComprobar si un elemento tiene una propiedad o estadohasPermission(), hasItemsInCart()
validateValidar datos o entradasvalidateForm(), validateEmail()
saveGuardar datos en una base de datos o almacenamientosaveUserSettings(), saveChanges()
subscribeSuscribirse a un observable o eventosubscribeToNotifications(), subscribeToNewsletter()
unsubscribeCancelar una suscripción de un observable o eventounsubscribeFromUpdates(), unsubscribeFromService()

📦 Dependencias

A continuación se enlistan las dependencias dentro de la arquitectura:

  • Prettier - Formateo de codificación
  • Eslint - Linteo y detección de errores en el código
  • Husky - Git Hooks para mejorar la calidad del código antes de realizar confirmaciones (git commit) o enviar cambios (git push)
  • Template - Template dinamico basado con estilos de bootstrap

Lintening

Para el linteo ocuparemos 2 formas de hacerlo, con Eslint mientras estamos codeando, y también con husky, al hacer un pre-commit.

A continuación se muestra el comando que se debera ocupar para lintear los archivos del todo el proyecto:

npm run lint

Y la otra forma de hacerlo, es cuando se hace un git commit convencional:

git commit -m "mensaje"

Esto detonora husky y hara un linteo por nosotros para los archivos que fueron modificados.


🔍Estandares de calidad

Para las pruebas unitarias se requiere un minimo de 85% de cobertura, para poder ser aceptado y posteriormente mergeado en un PR.

También se implementara la herramienta de Sonar para escanear y cuidar la calidad del codigo, y detectar posibles fallas o codesmells.


📏 Documentación (lineamientos)

Para la documentación del codigo, se debera ocupar JSDoc.

JSDoc es una herramienta que permite agregar comentarios estructurados en el código para documentar funciones, clases y propiedades de manera clara y comprensible. Esto facilita la colaboración en equipos y mejora la mantenibilidad del código.

A continuación se muestran diferentes ejemplos:

1. Metodo Simple

/**
* Calcula el total con descuento.
* @param {number} precio - Precio original del producto.
* @param {number} descuento - Porcentaje de descuento (0.10 = 10%).
* @returns {number} Total con descuento aplicado.
*/
calcularTotal(precio: number, descuento: number): number {
return precio - (precio * descuento);
}

Explicación:

@param {number} a y @param {number} b describen los parámetros de la función, ambos son números.

@returns {number} indica que la función devuelve un número, que es el resultado de la operación de los parametros.

2. Método que invoca otros métodos

/**
* Procesa la compra de un producto.
* Llama a métodos internos para calcular precio, generar factura y guardar en base de datos.
* @param {number} precio - Precio del producto.
* @param {number} descuento - Porcentaje de descuento.
* @returns {boolean} Devuelve `true` si la compra se procesó correctamente.
*/
procesarCompra(precio: number, descuento: number): boolean {
const total = this.calcularTotal(precio, descuento);
const factura = this.generarFactura(total);
return this.guardarFactura(factura);
}

/**
* Genera una factura con el total.
* @param {number} total - Total a facturar.
* @returns {{ total: number, fecha: Date }} Objeto factura.
*/
private generarFactura(total: number): { total: number; fecha: Date } {
return { total, fecha: new Date() };
}

/**
* Guarda la factura en la base de datos.
* @param {{ total: number, fecha: Date }} factura - Factura a guardar.
* @returns {boolean} `true` si se guardó correctamente.
*/
private guardarFactura(factura: { total: number; fecha: Date }): boolean {
console.log('Factura guardada:', factura);
return true;
}

3. Interfaz con varias propiedades

/**
* Representa un usuario en el sistema.
* @interface
*/
export interface IUsuario {
/** Identificador único del usuario. */
id: string;

/** Nombre completo del usuario. */
nombre: string;

/** Correo electrónico del usuario. */
email: string;

/** Fecha de creación del usuario. */
fechaCreacion: Date;

/** Indica si el usuario está activo. */
activo: boolean;
}

4. Constructor heredando funcionalidad

export class RemotoFormViewModel extends BaseViewModel {
/**
* Crea una instancia de `RemotoFormViewModel`, que gestiona la lógica y el estado
* del formulario para operaciones remotas.
*
* Esta clase extiende de `BaseViewModel`, heredando su funcionalidad base
* para el manejo de estados, validaciones y flujo de datos.
* La llamada a `super()` inicializa las propiedades y lógica definidas en la clase padre.
*/
constructor() {
super();
}
}

5. Inyecciónes de dependencias

/** Cliente HTTP para realizar peticiones a la API. */
private readonly http = inject(HttpClient);

/** Servicio de enrutamiento para navegar entre páginas. */
private readonly router = inject(Router);

/** Servicio de autenticación para obtener datos del usuario actual. */
private readonly authService = inject(AuthService);

/** Servicio para el registro y seguimiento de logs en la aplicación. */
private readonly logger = inject(LoggerService);

6. Declaración de variables públicas y privadas en un componente

/**
* Componente para mostrar el dashboard.
*/
export class DashboardComponent {
/** Título del dashboard que se muestra en la vista. */
public titulo: string = "Panel de control";

/** Lista de usuarios cargados desde la API. */
public usuarios: Usuario[] = [];

/** Estado interno que indica si se está cargando la información. */
private cargando: boolean = false;

/** URL base de la API. */
private readonly apiUrl: string = "https://api.midominio.com";
}
danger

❌ En este proyecto está prohibido el uso de comentarios de una sola línea con // dentro del código TypeScript, ya que no siguen el estándar de documentación adoptado y no son procesados por herramientas automáticas como JSDoc.

❌ En este proyecto está prohibido el uso de comentarios como // TODO y // FIXME

👍 Toda la documentación debera ser en español.