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

TipoConvenciónEjemplo
Archivos de componentesPascalCaseUserList.tsx, Button.astro
Archivos de hookscamelCase + prefijo useuseAuth.ts, useFetch.ts
Archivos de utilitiescamelCaseformatDate.ts, validation.ts
директорииkebab-caseuser-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.

Referencias