Organización Frontend
Estructura de carpetas y convenciones para proyectos Angular, React, Astro y Next.js.
Proyectos frontend — Estándares de organización para Angular, React, Astro y Next.js
Propósito
Esta guía define la estructura de carpetas y convenciones para proyectos frontend del DTI. El objetivo es garantizar consistencia independientemente del framework utilizado.
Angular
Estructura con Standalone Components
proyecto/
├── src/
│ ├── app/
│ │ ├── core/
│ │ │ ├── services/
│ │ │ │ ├── auth.service.ts
│ │ │ │ └── api.service.ts
│ │ │ ├── guards/
│ │ │ ├── interceptors/
│ │ │ └── models/
│ │ ├── shared/
│ │ │ ├── components/
│ │ │ │ ├── button/
│ │ │ │ └── modal/
│ │ │ ├── pipes/
│ │ │ └── directives/
│ │ ├── features/
│ │ │ ├── users/
│ │ │ │ ├── user-list/
│ │ │ │ ├── user-detail/
│ │ │ │ └── user.service.ts
│ │ │ └── dashboard/
│ │ └── layout/
│ │ ├── header/
│ │ ├── footer/
│ │ └── sidebar/
│ ├── assets/
│ └── styles/
├── angular.json
├── package.json
└── tsconfig.json
Ejemplo de component
// src/app/features/users/user-list/user-list.component.ts
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UserService } from './user.service';
import { User } from '@app/core/models/user';
@Component({
selector: 'app-user-list',
standalone: true,
imports: [CommonModule],
template: `
<h1>Users</h1>
<ul>
<li *ngFor="let user of users">
{{ user.name }} - {{ user.email }}
</li>
</ul>
`
})
export class UserListComponent {
private userService = inject(UserService);
users = this.userService.getUsers();
}
React con Vite
Estructura
proyecto/
├── src/
│ ├── components/
│ │ ├── ui/
│ │ │ ├── Button.tsx
│ │ │ └── Input.tsx
│ │ └── layout/
│ ├── pages/
│ │ ├── Home.tsx
│ │ ├── Users.tsx
│ │ └── NotFound.tsx
│ ├── hooks/
│ │ ├── useAuth.ts
│ │ └── useFetch.ts
│ ├── services/
│ │ └── api.ts
│ ├── store/
│ │ └── authStore.ts
│ ├── utils/
│ │ └── format.ts
│ ├── types/
│ │ └── user.ts
│ ├── App.tsx
│ └── main.tsx
├── vite.config.ts
├── package.json
└── tsconfig.json
Ejemplo de component
// src/pages/Users.tsx
import { useState, useEffect } from 'react';
import { Button } from '@components/ui/Button';
import { api } from '@services/api';
interface User {
id: string;
name: string;
email: string;
}
export function UsersPage() {
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
api.get<User[]>('/users').then(setUsers);
}, []);
return (
<div>
<h1>Users</h1>
<Button onClick={() => console.log('clicked')}>Add User</Button>
<ul>
{users.map(user => (
<li key={user.id}>{user.name} - {user.email}</li>
))}
</ul>
</div>
);
}
Astro
Estructura
proyecto/
├── src/
│ ├── components/
│ │ ├── Button.astro
│ │ ├── Card.astro
│ │ └── Header.astro
│ ├── layouts/
│ │ ├── BaseLayout.astro
│ │ └── ArticleLayout.astro
│ ├── pages/
│ │ ├── index.astro
│ │ ├── about.astro
│ │ └── blog/
│ ├── content/
│ │ ├── articles/
│ │ └── docs/
│ └── styles/
│ └── global.css
├── astro.config.mjs
├── package.json
└── tsconfig.json
Ejemplo de página
---
// src/pages/index.astro
import BaseLayout from '@layouts/BaseLayout.astro';
import Button from '@components/Button.astro';
const title = "Inicio";
---
<BaseLayout title={title}>
<h1>{title}</h1>
<Button>Click me</Button>
</BaseLayout>
Next.js (App Router)
Estructura
proyecto/
├── src/
│ ├── app/
│ │ ├── (auth)/
│ │ │ ├── login/
│ │ │ │ └── page.tsx
│ │ │ └── register/
│ │ ├── (dashboard)/
│ │ │ ├── layout.tsx
│ │ │ ├── users/
│ │ │ │ └── page.tsx
│ │ │ └── settings/
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ └── not-found.tsx
│ ├── components/
│ │ ├── ui/
│ │ └── forms/
│ ├── lib/
│ │ ├── api.ts
│ │ └── auth.ts
│ └── styles/
├── next.config.js
├── package.json
└── tsconfig.json
Ejemplo de página
// src/app/(dashboard)/users/page.tsx
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Users',
};
export default function UsersPage() {
return (
<div>
<h1>Users</h1>
</div>
);
}
Convenciones de naming
| Tipo | Convención | Ejemplo |
|---|---|---|
| Archivos de componentes | PascalCase | UserList.tsx, Button.astro |
| Archivos de hooks | camelCase + prefijo use | useAuth.ts, useFetch.ts |
| Archivos de utilities | camelCase | formatDate.ts, validation.ts |
| директории | kebab-case | user-list/, auth-forms/ |
Imports
Usar alias de path para imports absolutos:
// tsconfig.json paths
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@lib/*": ["./src/lib/*"]
}
}
}
Lazy loading
Usar lazy loading para componentes que no son críticos en el initial load:
// React
const HeavyComponent = lazy(() => import('./HeavyComponent'));
// Angular (standalone)
const routes: Routes = [
{ path: 'slow', loadComponent: () => import('./slow.component') }
];
Siguiente paso
Revisar Seguridad para conocer los lineamientos de seguridad en desarrollo frontend.