Estructura base para cualquier proyecto de Angular
En este artículo te mostraré la estructura de directorios y archivos ideal para cualquier proyecto de angular.
Luego de haber realizado varios proyectos con el framework de Angular y de haber buscado las mejores maneras de estructurar un proyecto adecuadamente, he conseguido una estructura para obtener un proyecto bien administrado, reutilizable, flexible para proyectos grandes o pequeños. Y el día te hoy te la voy a compartir 😃
Puedes encontrar un proyecto guía en este repositorio
https://github.com/baguilar6174/asesori-web-page
Para este artículo estoy usando Angular en su versión 15.0.2 con node en su versión 18.12.1
Comencemos creando un proyecto de Angular, para ello utilizamos el siguiente comando (puedes usar el cmd o la terminal que tú desees). En este caso usaré el cmd de Windows.
ng new <project-name>
Al digitar este comando, el cli de Angular te hará algunas preguntas
- Would you like to add Angular routing? (y/N)… Este paso es importante ya que te pregunta si deseas agregar el routing a tu aplicación. Este archivo es importante ya que nos permite gestionar las rutas en nuestra aplicación. Te recomiendo agregarlo.
- Which stylesheet format would you like to use? … Además te pregunta el formato de hoja de estilo que deseas agregar. Te recomiendo que uses SCSS, este tipo de hoja de estilo posee todas las ventajas de css incluidas las ventajas de SASS que como indica en su sitio oficial es un css con superpoderes 😎
🚧 A tener en cuenta: Cuando usas la terminal integrada de git, el comando de creación NO pregunta si se deseas el routing o que formato de estilos para el proyecto, los crea por defecto.
Una vez completes estos pasos, tu proyecto continuará con el proceso de creación. Ahora crearemos la estructura del proyecto (a partir de un proyecto recién creado).
Secciones de estructura de directorios
Se recomiendan que estas secciones se agreguen a una estructura de aplicación Angular (cada sección es opcional)
- media directory — Este directorio puede almacenar archivos de apoyo para la aplicación. Cosas como documentación, requisitos, esquemas, imágenes de la aplciación, etc.
- core module— Este módulo contiene clases utilizadas por el módulo principal de la aplicación app.module. Los recursos que siempre se cargan en nuestra aplicación, como guards de rutas, interceptores HTTP y servicios de nivel de aplicación.
- data module— El módulo de datos contiene los tipos (modelos / entidades) y servicios (repositorios) de los datos consumidos por la aplicación.
- layout directory— Este directorio contiene componentes que actúan como un diseño o son partes de un diseño como el header, footer, skeleton, etc.
- shared module — Este módulo contiene clases y recursos que se utilizan en más de un módulo cargado dinámicamente. Al cargar siempre con la aplicación, los componentes compartidos están listos cuando un módulo los solicita.
- modules directory— El directorio de módulos contiene una colección de módulos que son independientes entre sí. Esto permite que Angular cargue solo el módulo que requiere para mostrar la solicitud, lo que ahorra ancho de banda y acelera toda la aplicación.
- styles — Este directorio se utiliza para almacenar hojas de estilo scss para la aplicación. Puede contener temas, colores globales y cualquier otro estilo.
├── media
└── src
├── app
│ ├── core
│ ├── data
│ ├── layout
│ ├── modules
│ └── shared
├── assets
└── styles
Configuración para acceso a rutas de manera abreviada
En el archivo tsconfig.json agregar el array ‘paths’ (o reemplzar su contenido en caso de que exista). Esta configuración evita importaciones de este tipo ../../../<file-name>
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
...
"lib": [
"es2018",
"dom"
],
"paths": {
"@core/*": ["src/app/core/*"],
"@shared/*":["src/app/shared/*"],
"@components/*":["src/app/shared/*"],
"@env/*":["src/environments/*"],
"@modules/*":["src/app/modules/*"],
"@data/*":["src/app/data/*"],
"@layout/*":["src/app/layout/*"],
}
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
...
}
}
Generación de módulos y directorios
A continuación vamos a generar todos los directorios antes mencionados. Hay que tener en cuenta que todos estos comandos se deben realizar desde la raíz de tu aplicación.
Directorio de medios (media directory)
mkdir media
Módulo principal (core module)
ng g m core
Una vez creado el módulo, agrégalo en los imports del módulo principal de la aplicación app.module.ts (no olvides hacer la importación correspondiente)
// src\app\app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CoreModule } from '@core/core.module';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
CoreModule
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
🚧 A tener en cuenta: En este punto te recomiendo agregar el BrowserAnimationsModule este módulo ayuda a incorporar animaciones del navegador.
Módulo de datos (data module)
ng g m data
Una vez creado este módulo, es necesario crear los siguientes directorios dentro de módulo de datos.
- constants — directorio para almacenar constantes de la aplicación como datos estáticos, rutas de páginas, rutas de apis, etc.
- interfaces — contiene los modelos de aplicación en angular estos modelos se definen mediante entidades.
- mocks — contiene información predefinida. Nos permite verificar el funcionamiento de la aplicación
- services/api — servicios enfocados a la información (conexiones a apis y servicios)
El resultado de este módulo de datos y su estructura es el siguiente
Directorio de diseño (layout directory)
mkdir src/app/layout
Creado este directorio, es posible definir los elementos básicos de una aplicación web, en este caso definiremos el header, footer y skeleton de la aplicación. 🚧 Recuerda que estos comandos se ejecutan desde la raíz de tu aplicación.
ng g c layout/skeleton
ng g c layout/footer
ng g c layout/header
Puedes usar el argumento — skip-tests en los comandos, este argumento indica que estos componentes se generarán sin su archivos de pruebas. Puedes quitar este argumento y generar dicho archivo si deseas.
En este punto es importante que verifiques que los componentes creados se hayan importado en las declaraciones del archivo de módulo principal de tu aplicaicón app.module.ts
// src\app\app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CoreModule } from '@core/core.module';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SkeletonComponent } from './layout/skeleton/skeleton.component';
import { FooterComponent } from './layout/footer/footer.component';
import { HeaderComponent } from './layout/header/header.component';
@NgModule({
declarations: [
AppComponent,
SkeletonComponent,
FooterComponent,
HeaderComponent,
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
CoreModule
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
Si los componentes fueron importados correctamente en tu app.module.ts. Ve al archivo app.component.html y elimina todo su contenido, únicamente asegúrate de tener el enrutador de la aplicación:
<!-- src\app\app.component.html -->
<router-outlet></router-outlet>
Ahora vamos a agregar nuestros componentes de header y footer al skeleton de la aplicación.
<!-- src\app\layout\skeleton\skeleton.component.html -->
<!-- Header -->
<app-header></app-header>
<!-- Page -->
<router-outlet></router-outlet>
<!-- Footer -->
<app-footer></app-footer>
🚧 Ten en cuenta: El nombre de los selectores del header y footer dependerán del nombre con el que creaste dichos componentes.
Módulo compartido (shared module)
ng g m shared
Una vez creado este módulo, debemos agregarlo en los imports del módulo principal de la aplicación app.module.ts.
// src\app\app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CoreModule } from '@core/core.module';
import { SharedModule } from '@shared/shared.module';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SkeletonComponent } from './layout/skeleton/skeleton.component';
import { FooterComponent } from './layout/footer/footer.component';
import { HeaderComponent } from './layout/header/header.component';
@NgModule({
declarations: [
AppComponent,
SkeletonComponent,
FooterComponent,
HeaderComponent,
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
CoreModule,
SharedModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
El módulo compartido es un lugar bueno para importar y exportar el FormsModule
y el ReactiveFormsModule
para el uso de formularios en la aplicación, también es bueno para el HttpClientModule
y el RouterModule
para realizar solicitudes HTTP y moverse entre rutas del la aplicación.
El directorio de este módulo puede lucir de esta manera
En este punto te recomiendo que leas un poco sobre atomic design para crear y organizar los componentes de tu aplicación.
🚧 Ten en cuenta: El uso de archivos de barril puede resultar muy útil para abreviar las importaciones.
// src\app\shared\shared.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import * as components from './components';
@NgModule({
imports: [
FormsModule,
ReactiveFormsModule,
HttpClientModule,
RouterModule,
CommonModule
],
declarations: [...components.components],
exports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
RouterModule,
...components.components
],
})
export class SharedModule {}
Directorio de módulos (module directory)
Este directorio contiene los módulos que son independientes entre sí. Esto permite que Angular cargue solo el módulo que requiere para mostrar la solicitud, lo que ahorra ancho de banda y acelera toda la aplicación. Ya que se hace uso de Lazy Loading.
Para lograr esto, cada módulo debe tener su propio enrutamiento, que es un recurso loadChildren
de ruta definido en el AppRoutingModule
.
Para nuestra aplicación vamos a generar tres módulos, un home (dashboard), contact (contacto) y about (acerca de).
ng g m modules/<module-name> --routing
Para nuestro ejemplo:
ng g m modules/home --routing
ng g m modules/contact --routing
ng g m modules/about --routing
🚧 Ten en cuenta: Cada vez que generamos un módulo es necesario importar nuestro SharedModule como se muestra a continuación:
import { NgModule } from '@angular/core';
import { HomeRoutingModule } from './home-routing.module';
import { SharedModule } from '@shared/shared.module';
import { HomeComponent } from './page/home.component';
import { components } from './components';
@NgModule({
declarations: [HomeComponent, ...components],
imports: [SharedModule, HomeRoutingModule],
})
export class HomeModule {}
Generar un componente de página principal para cada módulo
Ahora vamos a crear las páginas principales de cada módulo (dashboard, contact, about).
ng g c modules/<module-name>/pages/<page-name>
Para nuestro ejemplo:
ng g c modules/about/pages/about
ng g c modules/contact/pages/contact
ng g c modules/home/pages/home
🚧 Ten en cuenta: Una vez hemos creado nuestros componentes de página principal, debemos establecer la ruta del componente principal del módulo creado. En el routing de cada módulo:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './page/home.component';
const routes: Routes = [{ path: '', component: HomeComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class HomeRoutingModule {}
Carga de módulos con LazyLoading
Para inyectar nuestros módulos creados mediante lazy loading:
// src\app\app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { EMPTY_STRING, INTERNAL_PATHS } from '@data/constants/routes';
import { SkeletonComponent } from '@layout/skeleton/skeleton.component';
import { HomeModule } from '@modules/home/home.module';
const routes: Routes = [
{
path: EMPTY_STRING,
component: SkeletonComponent,
children: [
{
path: INTERNAL_PATHS.APP_DEFAULT,
loadChildren: () => import('@modules/home/home.module').then((m): typeof HomeModule => m.HomeModule),
},
{ path: '**', redirectTo: EMPTY_STRING, pathMatch: 'full' },
],
},
{ path: '**', redirectTo: EMPTY_STRING, pathMatch: 'full' },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Configuración para la recarga de nuestras rutas
Esta configuración es importante, ya que nos permitirá un correcto funcionamiento en la recarga de páginas y rutas de nuestra aplicación cuando nuestro proyecto se encuentre en producción o un hosting.
En el módulo principal de muestra aplicación app.module.ts en el apartado de providers, agregamos la estrategia para la recarga de las rutas:
// src\app\app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { LocationStrategy, PathLocationStrategy } from '@angular/common';
import { CoreModule } from '@core/core.module';
import { SharedModule } from '@shared/shared.module';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SkeletonComponent } from './layout/skeleton/skeleton.component';
import { FooterComponent } from './layout/footer/footer.component';
import { HeaderComponent } from './layout/header/header.component';
@NgModule({
declarations: [
AppComponent,
SkeletonComponent,
FooterComponent,
HeaderComponent,
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
CoreModule,
SharedModule,
],
providers: [
{
provide: LocationStrategy,
useClass: PathLocationStrategy,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
Por otro lado, en el archivo app-routing.module.ts en el apartado de imports agregar lo siguiente
// src\app\app-routing.module.ts
...
@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule],
})
export class AppRoutingModule {}
Comprobación
En este punto podemos verificar que la aplicación funcione correctamente, podemos ejecutar alguno de los siguientes comandos
ng serve
ng serve -o
npm run start
Nuestra aplicación esta funcionando correctamente TE FELICITO 🥳🥳, si deseas puedes seguir leyendo otras consideraciones que son importantes para tener tu proyecto como todo un pro 😎.
Otras consideraciones
Cuando genero un nuevo módulo, este contiene dos directorios, uno de páginas y otro de componentes. El directorio de páginas contiene los componentes que se comportan como páginas de nuestro módulo, minetras que el directorio de componentes contiene esos componentes que se pueden reutilizar en las páginas de nuestro módulo (ejemplo: títulos, botones, cards, etc).
Para generar estos componentes puedes usar este comando
ng g c modules/<module-name>/components/<component-name>
Uso de Git
Se recomienda el uso de Git para los proyectos (que dependan de trabajo colaborativo), inicialmente se recomienda crear tres ramas:
- master/main: código completamente funcional o producción
- staging: funciona como entorno de pruebas. Una vez se pase de develop a esta rama, únicamente se harán pruebas
- develop: todo el desarrollo se hace aquí, los distintos desarrolladores sacan sus cambios desde esta rama.
git checkout -b staging
git checkout -b develop
Debe estar posicionado en develop para iniciar el desarrollo.
Herramientas de formato y calidad del código
Con el fin de establecer un estándar que será utilizado por todos los contribuyentes al proyecto para mantener el estilo de código coherente y las mejores prácticas básicas seguidas vamos a implementar dos herramientas:
- eslint — Para las mejores prácticas en los estándares de codificación
- prettier — Para el formateo automático de archivos de código
Git Hooks
Recuerda que vas a querer que este proyecto sea lo más sólido posible si vas a construir sobre él a largo plazo, particularmente con un equipo de otros desarrolladores. Vale la pena el tiempo para hacerlo bien desde el principio.
Te recomiendo usar Husky, es una herramienta para ejecutar scripts en diferentes etapas del proceso git, por ejemplo add, commit, push, etc. Nos gustaría poder establecer ciertas condiciones, y sólo permitir cosas como commit y push para tener éxito si nuestro código cumple con esas condiciones, suponiendo que indica que nuestro proyecto es de calidad aceptable.
Internacionalización de aplicaciones
Hoy en día es muy común ver que los sitios web poseen traducciones a múltiples idiomas, este es un aspecto a tener en cuenta a la hora de iniciar con un proyecto ya que el implementarlo desde el inicio puede ahorrarte muchas horas de trabajo y esfuerzo. Existen herramientas como ngx-translate o transloco que te pueden ayudar en esta tarea, te recomiendo darles un vistazo.
Atomic design
Otro aspecto a tomar en cuenta es unar la metodología de atomic design para diseñar, crear y organizar tus componentes.
La importancia de tener una estructura base en tu proyecto es esencial, así que te dejo el repositorio de GitHub🤓 para que puedas tomarlo como referencia para tus proyectos con Angular, recuerda que antes debes tener todo configurado (angular y node instalados) para que funcione correctamente.
Estas son las fuentes que he usado para realizar este artículo
Código fuente de este proyecto https://github.com/baguilar6174/angular-structure-proyect
Puedes encontrar este y otros en mi repositorio de Github . No olvides visitar mi página web.
¡Gracias por leer este artículo!
Si deseas hacerme alguna pregunta, no lo dudes!. Mi bandeja de entrada siempre estará abierta. Ya sea que tengas una pregunta o simplemente me quieras saludar, ¡haré todo lo posible para responderle!