Saltar al contenido principal

Documentation Index

Fetch the complete documentation index at: https://blog.mapptech.com.co/llms.txt

Use this file to discover all available pages before exploring further.

Introducción

Construir aplicaciones de nivel empresarial con Next.js requiere una cuidadosa consideración de la arquitectura, el rendimiento, la seguridad y la mantenibilidad. Esta guía proporciona una visión general completa de las mejores prácticas para desarrollar aplicaciones Next.js robustas que puedan escalar para satisfacer las demandas empresariales.

1. Arquitectura y Escalabilidad

Arquitectura Modular

Organiza tu aplicación Next.js utilizando una arquitectura modular para mejorar la mantenibilidad y la escalabilidad:
/src
  /app                 # App Router pages and layouts
  /components          # Reusable UI components
    /ui                # Base UI components
    /features          # Feature-specific components
  /lib                 # Utility functions and shared code
  /hooks               # Custom React hooks
  /services            # External service integrations
  /types               # TypeScript type definitions
  /styles              # Global styles and theme configuration
  /middleware          # Next.js middleware

División de Código (Code Splitting) y Carga Diferida (Lazy Loading)

Next.js divide automáticamente el código por ruta, pero puedes optimizar aún más con importaciones dinámicas:
// Dynamic import with loading state
import dynamic from 'next/dynamic';

const DynamicDashboard = dynamic(() => import('@/components/Dashboard'), {
  loading: () => <p>Cargando panel...</p>, //<- Traducido
  ssr: false // Disable SSR if component relies on browser APIs
});

export default function Page() {
  return <DynamicDashboard />;
}

Obtención de Datos Eficiente

Aprovecha los React Server Components para una obtención de datos eficiente:
// app/products/page.tsx
import { ProductList } from '@/components/ProductList';
import { getProducts } from '@/lib/products';

export default async function ProductsPage() {
  // This runs on the server and doesn't send unnecessary data to the client
  const products = await getProducts();

  return <ProductList products={products} />;
}
Para la obtención de datos del lado del cliente, usa SWR o React Query:
'use client';

import { useQuery } from '@tanstack/react-query';

function ProductsClient() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['products'],
    queryFn: () => fetch('/api/products').then(res => res.json())
  });

  if (isLoading) return <div>Cargando...</div>; //<- Traducido
  if (error) return <div>Error al cargar productos</div>; //<- Traducido

  return (
    <ul>
      {data.map(product => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  );
}

Modelos de Despliegue

Despliegue Serverless

Los despliegues serverless (como Vercel) ofrecen escalado automático y reducen la carga operativa:
// next.config.js for optimized serverless deployment
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone', // Creates a standalone build optimized for containerized environments
}

module.exports = nextConfig;

Despliegue Contenerizado

Para despliegues en Kubernetes o Docker, usa el modo de salida standalone:
# Dockerfile for Next.js
FROM node:18-alpine AS base

# Install dependencies only when needed
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production

# Create a non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy standalone build
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

USER nextjs

EXPOSE 3000

ENV PORT 3000

CMD ["node", "server.js"]

Estrategias de Base de Datos y Caché

Selección de Base de Datos

Elige la base de datos adecuada según tus necesidades:
  • PostgreSQL: Para datos relacionales complejos con cumplimiento ACID
  • MongoDB: Para esquemas flexibles y datos orientados a documentos
  • Redis: Para caché y funcionalidades en tiempo real
  • Supabase/Firebase: Para desarrollo rápido con autenticación incorporada

Implementación de Caché

Implementa caché multinivel:
// Route segment caching
export const revalidate = 3600; // Revalidate every hour

// Data cache with fetch
async function getProducts() {
  const res = await fetch('https://api.example.com/products', {
    next: { revalidate: 3600 } // Cache for 1 hour
  });

  return res.json();
}

// Request memoization
import { cache } from 'react';

export const getUser = cache(async (id: string) => {
  const user = await db.user.findUnique({ where: { id } });
  return user;
});

2. Optimización del Rendimiento

Optimización de Imágenes

Usa el componente Image de Next.js para optimización automática:
import Image from 'next/image';

export default function ProductImage({ product }) {
  return (
    <div className="relative h-64 w-full">
      <Image
        src={product.imageUrl || "/placeholder.svg"}
        alt={product.name}
        fill
        sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
        priority={product.featured}
        className="object-cover rounded-lg"
      />
    </div>
  );
}

Optimización de Fuentes

Optimiza las fuentes utilizando el sistema de fuentes incorporado de Next.js:
// app/layout.tsx
import { Inter, Roboto_Mono } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
});

const robotoMono = Roboto_Mono({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-roboto-mono',
});

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    // Cambiado lang="en" a lang="es"
    <html lang="es" className={`${inter.variable} ${robotoMono.variable}`}>
      <body>{children}</body>
    </html>
  );
}

Estrategias de Renderizado

Elige la estrategia de renderizado adecuada según tu contenido:
// Static page with generated static params
export async function generateStaticParams() {
  const products = await getProducts();

  return products.map((product) => ({
    id: product.id,
  }));
}

// Dynamic page with ISR
export const revalidate = 60; // Revalidate at most once per minute

// Dynamic page with on-demand revalidation
// pages/api/revalidate.js
export default async function handler(req, res) {
  if (req.headers.authorization !== `Bearer ${process.env.REVALIDATION_TOKEN}`) {
    // Traducido
    return res.status(401).json({ message: 'Token inválido' });
  }

  try {
    await res.revalidate('/products');
    return res.json({ revalidated: true });
  } catch (err) {
    // Traducido
    return res.status(500).send('Error al revalidar');
  }
}

Monitorización del Rendimiento

Implementa la monitorización del rendimiento con herramientas como Web Vitals:
'use client';

import { useReportWebVitals } from 'next/web-vitals';

export function WebVitalsReporter() {
  useReportWebVitals(metric => {
    // Send to analytics
    console.log(metric);

    // Example: send to Google Analytics
    const analyticsId = 'UA-XXXXX-Y';
    const body = JSON.stringify({
      name: metric.name,
      value: metric.value,
      id: metric.id,
    });

    navigator.sendBeacon(`https://www.google-analytics.com/collect?v=1&t=event&ec=Web%20Vitals&ea=${metric.name}&el=${metric.id}&ev=${metric.value}&tid=${analyticsId}`, body);
  });

  return null;
}

3. Mejores Prácticas de Seguridad

Validación de Entradas

Valida siempre las entradas del usuario tanto en el cliente como en el servidor:
// Server-side validation in a Server Action
'use server';

import { z } from 'zod';

const FormSchema = z.object({
   // Traducido
  email: z.string().email('Dirección de correo inválida'),
   // Traducido
  password: z.string().min(8, 'La contraseña debe tener al menos 8 caracteres'),
});

export async function createUser(prevState: any, formData: FormData) {
  const validatedFields = FormSchema.safeParse({
    email: formData.get('email'),
    password: formData.get('password'),
  });

  if (!validatedFields.success) {
    return {
      errors: validatedFields.error.flatten().fieldErrors,
       // Traducido
      message: 'Campos faltantes. No se pudo crear el usuario.',
    };
  }

  // Proceed with creating user...
}

Seguridad de API

Asegura tus rutas de API con autenticación adecuada y limitación de tasa (rate limiting):
// app/api/protected/route.ts
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { NextResponse } from 'next/server';
import { rateLimit } from '@/lib/rate-limit';

export async function GET(request: Request) {
  const session = await getServerSession(authOptions);

  if (!session) {
     // Traducido
    return NextResponse.json({ error: 'No autorizado' }, { status: 401 });
  }

  // Apply rate limiting
  const limiter = rateLimit({
    interval: 60 * 1000, // 1 minute
    uniqueTokenPerInterval: 500,
  });

  try {
    await limiter.check(10, session.user.id); // 10 requests per minute per user
  } catch {
     // Traducido
    return NextResponse.json({ error: 'Límite de tasa excedido' }, { status: 429 });
  }

  // Process the request
   // Traducido
  return NextResponse.json({ data: 'Datos protegidos' });
}

Autenticación y Autorización

Implementa autenticación robusta con NextAuth.js:
// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { compare } from 'bcrypt';
import { prisma } from '@/lib/prisma';

export const authOptions = {
  providers: [
    CredentialsProvider({
       // Traducido
      name: 'Credenciales',
      credentials: {
         // Traducido
        email: { label: 'Email', type: 'email' },
         // Traducido
        password: { label: 'Contraseña', type: 'password' }
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          return null;
        }

        const user = await prisma.user.findUnique({
          where: { email: credentials.email }
        });

        if (!user) {
          return null;
        }

        const isPasswordValid = await compare(credentials.password, user.password);

        if (!isPasswordValid) {
          return null;
        }

        return {
          id: user.id,
          email: user.email,
          name: user.name,
          role: user.role,
        };
      }
    })
  ],
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.role = user.role;
      }
      return token;
    },
    async session({ session, token }) {
      session.user.role = token.role;
      return session;
    }
  },
  pages: {
    signIn: '/auth/signin',
    error: '/auth/error',
  },
  session: {
    strategy: 'jwt',
  },
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

Protección CSRF

Implementa protección CSRF (Cross-Site Request Forgery) para formularios:
'use client';

import { useState, useEffect } from 'react'; // Añadido useEffect que faltaba en original
import { getCsrfToken } from 'next-auth/react';

export default function ContactForm() {
  const [csrfToken, setCsrfToken] = useState('');

  // Añadido useEffect que faltaba en original
  useEffect(() => {
    async function fetchCsrfToken() {
      const token = await getCsrfToken();
      setCsrfToken(token || '');
    }
    fetchCsrfToken();
  }, []);

  return (
    <form method="post" action="/api/contact">
      <input name="csrfToken" type="hidden" value={csrfToken} />
      {/* Form fields */}
    </form>
  );
}

Política de Seguridad de Contenido (CSP)

Implementa una Política de Seguridad de Contenido estricta a través de middleware:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const response = NextResponse.next();

  // Add security headers
  response.headers.set('Content-Security-Policy',
    "default-src 'self'; " +
    "script-src 'self' 'unsafe-inline' https://analytics.example.com; " +
    "style-src 'self' 'unsafe-inline'; " +
    "img-src 'self' data: https://images.example.com; " +
    "font-src 'self'; " +
    "connect-src 'self' https://api.example.com; " +
    "frame-src 'none'; " +
    "object-src 'none';"
  );

  response.headers.set('X-XSS-Protection', '1; mode=block');
  response.headers.set('X-Frame-Options', 'DENY');
  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
  response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');

  return response;
}

export const config = {
  matcher: '/((?!api/auth|_next/static|_next/image|favicon.ico).*)',
};

4. Calidad del Código y Mantenibilidad

Integración con TypeScript

Usa TypeScript para seguridad de tipos y una mejor experiencia de desarrollo:
// types/index.ts
export interface User {
  id: string;
  name: string;
  email: string;
   // Traducido 'admin' | 'user' a 'admin' | 'usuario' - CORRECCIÓN: Mantener original
  role: 'admin' | 'user';
  createdAt: Date;
}

export interface Product {
  id: string;
  name: string;
  description: string;
  price: number;
  imageUrl: string;
  category: string;
  featured: boolean;
}

Linting y Formateo

Configura ESLint y Prettier para un estilo de código consistente:
// .eslintrc.json
{
  "extends": [
    "next/core-web-vitals",
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier"
  ],
  "plugins": ["@typescript-eslint"],
  "rules": {
    "no-unused-vars": "off",
    "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
    "react/no-unescaped-entities": "off",
    "@typescript-eslint/no-explicit-any": "warn",
    "react-hooks/exhaustive-deps": "warn"
  }
}
// .prettierrc
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "tabWidth": 2,
  "printWidth": 100
}

Estrategia de Pruebas

Implementa una estrategia de pruebas completa:
// Unit test with Jest and React Testing Library
// __tests__/components/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import Button from '@/components/ui/Button';

describe('Button component', () => {
  it('renders correctly', () => {
     // Traducido
    render(<Button>Haz clic</Button>);
     // Traducido
    expect(screen.getByRole('button', { name: /haz clic/i })).toBeInTheDocument();
  });

  it('calls onClick handler when clicked', () => {
    const handleClick = jest.fn();
     // Traducido
    render(<Button onClick={handleClick}>Haz clic</Button>);
     // Traducido
    fireEvent.click(screen.getByRole('button', { name: /haz clic/i }));
    expect(handleClick).toHaveBeenCalledTimes(1);
  });
});
// Integration test with Cypress
// cypress/e2e/authentication.cy.ts
describe('Authentication', () => {
  it('should allow a user to sign in', () => {
    cy.visit('/auth/signin');
    cy.get('input[name="email"]').type('user@example.com');
    cy.get('input[name="password"]').type('password123');
    cy.get('button[type="submit"]').click();
    cy.url().should('include', '/dashboard');
     // Traducido
    cy.contains('Bienvenido de nuevo').should('be.visible');
  });
});

Documentación de Componentes

Documenta tus componentes con Storybook:
// stories/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '@/components/ui/Button';

const meta: Meta<typeof Button> = {
  title: 'UI/Button',
  component: Button,
  parameters: {
    layout: 'centered',
  },
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'],
    },
  },
};

export default meta;
type Story = StoryObj<typeof Button>;

export const Primary: Story = {
  args: {
     // Traducido
    children: 'Botón',
    variant: 'default',
  },
};

export const Destructive: Story = {
  args: {
     // Traducido
    children: 'Eliminar',
    variant: 'destructive',
  },
};

5. Despliegue y DevOps

Pipeline de CI/CD

Configura un pipeline de CI/CD (Integración Continua / Despliegue Continuo) con GitHub Actions:
# .github/workflows/ci.yml
name: CI/CD Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm run test

  build:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      - run: npm ci
      - run: npm run build
      - name: Upload build artifact
        uses: actions/upload-artifact@v3
        with:
          name: build
          path: .next

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Download build artifact
        uses: actions/download-artifact@v3
        with:
          name: build
          path: .next
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v20
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'

Configuración Multi-Entorno

Configura ajustes específicos para cada entorno:
.env                # Default environment variables
.env.local          # Local overrides (not committed)
.env.development    # Development environment
.env.test           # Test environment
.env.production     # Production environment

Monitorización y Registro (Logging)

Implementa monitorización de aplicaciones con Sentry:
// app/monitoring.ts - CORRECCIÓN: Nombre de archivo diferente al original
// Original: No tenía un archivo específico, sino la config de Sentry.
// Revertir a una traducción directa del fragmento original.
// (El fragmento original en inglés no era una configuración completa,
// solo un ejemplo de inicialización y captura de errores).

// Manteniendo el código original, aunque no sea una config completa:
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 1.0,
  environment: process.env.NODE_ENV,
  // El original tenía un objeto Integrations.Http, mantenerlo
  integrations: [
     // @ts-ignore - Puede requerir ignorar si Sentry o las types cambiaron
    new Sentry.Integrations.Http({ tracing: true }),
  ],
});

// Instrument Next.js error handling
export function onRequestError({ error, request, context }) {
  Sentry.captureException(error, {
    extra: {
      requestUrl: request.url,
      context,
    },
  });
}

export function register() {
  // Initialize any other monitoring tools
}

6. Gestión de Estado y Obtención de Datos

Gestión del Estado del Servidor

Usa React Query para la gestión del estado del servidor:
// lib/providers.tsx
'use client';

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useState } from 'react';

export function Providers({ children }: { children: React.ReactNode }) {
  const [queryClient] = useState(() => new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 60 * 1000, // 1 minute
        retry: 1,
        refetchOnWindowFocus: process.env.NODE_ENV === 'production',
      },
    },
  }));

  return (
    <QueryClientProvider client={queryClient}>
      {children}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

Gestión del Estado del Cliente

Usa Zustand para la gestión del estado del lado del cliente:
// store/useCartStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface CartItem {
  id: string;
  name: string;
  price: number;
  quantity: number;
}

interface CartStore {
  items: CartItem[];
  addItem: (item: Omit<CartItem, 'quantity'>) => void;
  removeItem: (id: string) => void;
  updateQuantity: (id: string, quantity: number) => void;
  clearCart: () => void;
  totalItems: () => number;
  totalPrice: () => number;
}

export const useCartStore = create<CartStore>()(
  persist(
    (set, get) => ({
      items: [],
      addItem: (item) => set((state) => {
        const existingItem = state.items.find((i) => i.id === item.id);
        if (existingItem) {
          return {
            items: state.items.map((i) =>
              i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
            ),
          };
        }
        return { items: [...state.items, { ...item, quantity: 1 }] };
      }),
      removeItem: (id) => set((state) => ({
        items: state.items.filter((i) => i.id !== id),
      })),
      updateQuantity: (id, quantity) => set((state) => ({
        items: state.items.map((i) =>
          i.id === id ? { ...i, quantity } : i
        ),
      })),
      clearCart: () => set({ items: [] }),
      totalItems: () => get().items.reduce((acc, item) => acc + item.quantity, 0),
      totalPrice: () => get().items.reduce((acc, item) => acc + (item.price * item.quantity), 0),
    }),
    {
      name: 'cart-storage',
    }
  )
);

Patrones de Obtención de Datos

Implementa patrones eficientes para la obtención de datos:
// Parallel data fetching
async function ParallelDataFetching() {
  // Fetch data in parallel
  const productsPromise = getProducts();
  const categoriesPromise = getCategories();
  const featuredPromise = getFeaturedProducts();

  // Wait for all promises to resolve
  const [products, categories, featured] = await Promise.all([
    productsPromise,
    categoriesPromise,
    featuredPromise,
  ]);

  return (
    <div>
      <FeaturedProducts products={featured} />
      <CategoryNav categories={categories} />
      <ProductGrid products={products} />
    </div>
  );
}

// Sequential data fetching (when one request depends on another)
async function SequentialDataFetching() {
  // First fetch categories
  const categories = await getCategories();

  // Then fetch products for the first category
  const products = categories.length > 0
    ? await getProductsByCategory(categories[0].id)
    : [];

  return (
    <div>
      <CategoryNav categories={categories} />
      <ProductGrid products={products} />
    </div>
  );
}

7. Manejo de Errores y Monitorización

Manejo Global de Errores

Implementa límites de error globales:
// app/global-error.tsx
'use client';

import { useEffect } from 'react';
import * as Sentry from '@sentry/nextjs';

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // Log the error to Sentry
    Sentry.captureException(error);
  }, [error]);

  return (
    <html>
      <body>
        <div className="flex min-h-screen flex-col items-center justify-center p-4 text-center">
           {/* Traducido */}
          <h1 className="text-4xl font-bold mb-4">¡Algo salió mal!</h1>
           {/* Traducido */}
          <p className="mb-8 text-gray-600">
            Hemos sido notificados sobre este problema y estamos trabajando para solucionarlo.
          </p>
          <button
            onClick={() => reset()}
            className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
          >
             {/* Traducido */}
            Intentar de nuevo
          </button>
        </div>
      </body>
    </html>
  );
}

Manejo de Errores Específico de Ruta

Implementa límites de error específicos para rutas:
// app/dashboard/error.tsx
'use client';

import { useEffect } from 'react';
import * as Sentry from '@sentry/nextjs';
import { Button } from '@/components/ui/button'; // Corregido import, original era minúscula

export default function DashboardError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // Log the error to Sentry
    Sentry.captureException(error);
  }, [error]);

  return (
    <div className="p-8 text-center">
       {/* Traducido */}
      <h2 className="text-2xl font-bold mb-4">Error en el Panel</h2>
       {/* Traducido */}
      <p className="mb-4 text-gray-600">
        Ocurrió un error al cargar el panel.
      </p>
      <div className="flex justify-center gap-4">
         {/* Traducido */}
        <Button onClick={() => reset()}>Intentar de nuevo</Button>
        <Button variant="outline" onClick={() => window.location.href = '/'}>
           {/* Traducido */}
          Ir al Inicio
        </Button>
      </div>
    </div>
  );
}

Registro Estructurado (Logging)

Implementa logging estructurado:
// lib/logger.ts
type LogLevel = 'debug' | 'info' | 'warn' | 'error';

interface LogEntry {
  level: LogLevel;
  message: string;
  timestamp: string;
  context?: Record<string, any>;
}

class Logger {
  private context: Record<string, any> = {};

  constructor(context: Record<string, any> = {}) {
    this.context = context;
  }

  private log(level: LogLevel, message: string, additionalContext: Record<string, any> = {}) {
    const timestamp = new Date().toISOString();
    const entry: LogEntry = {
      level,
      message,
      timestamp,
      context: {
        ...this.context,
        ...additionalContext,
      },
    };

    // In development, log to console
    if (process.env.NODE_ENV === 'development') {
      console[level === 'debug' ? 'log' : level](JSON.stringify(entry, null, 2));
    } else {
      // In production, send to logging service
      // Example: send to Datadog, Loggly, etc.

      // For now, still log to console in production
      console[level === 'debug' ? 'log' : level](JSON.stringify(entry));
    }

    return entry;
  }

  debug(message: string, context?: Record<string, any>) {
    return this.log('debug', message, context);
  }

  info(message: string, context?: Record<string, any>) {
    return this.log('info', message, context);
  }

  warn(message: string, context?: Record<string, any>) {
    return this.log('warn', message, context);
  }

  error(message: string, error?: Error, context?: Record<string, any>) {
    return this.log('error', message, {
      ...context,
      error: error ? {
        message: error.message,
        stack: error.stack,
        name: error.name,
      } : undefined,
    });
  }

  withContext(additionalContext: Record<string, any>) {
    return new Logger({
      ...this.context,
      ...additionalContext,
    });
  }
}

export const logger = new Logger({
  service: 'next-app',
  environment: process.env.NODE_ENV,
});

8. Accesibilidad (a11y) e Internacionalización (i18n)

Implementación de Accesibilidad (a11y)

Asegura que tu aplicación sea accesible:
// components/ui/Button.tsx
import { forwardRef } from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none',
  {
    variants: {
      variant: {
        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
        destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
        outline: 'border border-input hover:bg-accent hover:text-accent-foreground',
        secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
        ghost: 'hover:bg-accent hover:text-accent-foreground',
        link: 'underline-offset-4 hover:underline text-primary',
      },
      size: {
        default: 'h-10 py-2 px-4',
        sm: 'h-9 px-3 rounded-md',
        lg: 'h-11 px-8 rounded-md',
        icon: 'h-10 w-10',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'default',
    },
  }
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    return (
      <button
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    );
  }
);
Button.displayName = 'Button';

export { Button, buttonVariants };

Implementación de Atributos ARIA

// components/ui/Dialog.tsx
'use client';

import * as React from 'react';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { X } from 'lucide-react';
import { cn } from '@/lib/utils';

const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;

const DialogOverlay = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Overlay>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
  <DialogPrimitive.Overlay
    ref={ref}
    className={cn(
      'fixed inset-0 z-50 bg-black/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
      className
    )}
    {...props}
  />
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;

const DialogContent = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <DialogPortal>
    <DialogOverlay />
    <DialogPrimitive.Content
      ref={ref}
      className={cn(
        'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
        className
      )}
      {...props}
    >
      {children}
      <DialogPrimitive.Close
        className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
         // Traducido
        aria-label="Cerrar diálogo"
      >
        <X className="h-4 w-4" />
         {/* Traducido */}
        <span className="sr-only">Cerrar</span>
      </DialogPrimitive.Close>
    </DialogPrimitive.Content>
  </DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;

// Mantener exportación original
export { Dialog, DialogTrigger, DialogContent, DialogClose };

Internacionalización (i18n)

Implementa internacionalización con next-intl:
// middleware.ts
import createMiddleware from 'next-intl/middleware';

export default createMiddleware({
  // A list of all locales that are supported
  locales: ['en', 'es', 'fr', 'de'],

  // If this locale is matched, pathnames work without a prefix (e.g. `/about`)
  defaultLocale: 'en',

  // Domains can be used to match specific locales
  domains: [
    {
      domain: 'example.com',
      defaultLocale: 'en'
    },
    {
      domain: 'example.es',
      defaultLocale: 'es'
    }
  ]
});

export const config = {
  // Skip all paths that should not be internationalized
  matcher: ['/((?!api|_next|.*\\..*).*)']
};
// messages/en.json (Archivo de inglés original)
{
  "Index": {
    "title": "Hello world!",
    "description": "This is a sample application"
  },
  "Navigation": {
    "home": "Home",
    "about": "About",
    "products": "Products",
    "contact": "Contact"
  },
  "Auth": {
    "signIn": "Sign In",
    "signUp": "Sign Up",
    "email": "Email",
    "password": "Password",
    "forgotPassword": "Forgot Password?"
  }
}
// messages/es.json (Archivo de español traducido)
{
  "Index": {
    "title": "¡Hola mundo!",
    "description": "Esta es una aplicación de ejemplo"
  },
  "Navigation": {
    "home": "Inicio",
    "about": "Acerca de",
    "products": "Productos",
    "contact": "Contacto"
  },
  "Auth": {
    "signIn": "Iniciar Sesión",
    "signUp": "Registrarse",
    "email": "Correo electrónico",
    "password": "Contraseña",
    "forgotPassword": "¿Olvidaste tu contraseña?"
  }
}
// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl';
import { notFound } from 'next/navigation';

export function generateStaticParams() {
  return [{ locale: 'en' }, { locale: 'es' }, { locale: 'fr' }, { locale: 'de' }];
}

export default async function LocaleLayout({
  children,
  params: { locale }
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  let messages;
  try {
    messages = (await import(`../../messages/${locale}.json`)).default;
  } catch (error) {
    notFound();
  }

  return (
    // Provider necesita lang={locale} en <html>, no aquí
    // El html tag está en el RootLayout superior usualmente
    <NextIntlClientProvider locale={locale} messages={messages}>
      {children}
    </NextIntlClientProvider>
  );
}
// app/[locale]/page.tsx
import { useTranslations } from 'next-intl';

export default function Index() {
  const t = useTranslations('Index');

  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('description')}</p>
    </div>
  );
}

9. Colaboración en Equipo y Flujo de Trabajo de Desarrollo

Flujo de Trabajo con Git

Implementa un flujo de trabajo robusto con Git:
# Feature branch workflow
git checkout -b feature/user-authentication
# Make changes
git add .
git commit -m "feat: implement user authentication"
# Push to remote
git push -u origin feature/user-authentication
# Create pull request
# After review and approval, merge to main

Commits Convencionales

Usa Commits Convencionales para mejor generación de changelogs:
feat: add user authentication
fix: resolve issue with password reset
docs: update README with setup instructions
style: format code according to style guide
refactor: simplify product filtering logic
test: add tests for cart functionality
chore: update dependencies

Directrices para la Revisión de Código

Establece directrices claras para la revisión de código:
  1. Funcionalidad: ¿El código funciona como se espera?
  2. Seguridad: ¿Hay alguna vulnerabilidad de seguridad?
  3. Rendimiento: ¿Está el código optimizado para el rendimiento?
  4. Mantenibilidad: ¿Es el código fácil de entender y mantener?
  5. Pruebas: ¿Hay suficientes pruebas?
  6. Documentación: ¿Está el código bien documentado?

Gestión de Dependencias

Gestiona las dependencias de forma eficaz:
// package.json
{
  "name": "enterprise-nextjs-app",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "test": "jest",
    "test:watch": "jest --watch",
    "e2e": "cypress run",
    "e2e:open": "cypress open",
    "format": "prettier --write .",
    "prepare": "husky install",
    "check-deps": "npx npm-check-updates"
  },
  "dependencies": {
    "@sentry/nextjs": "^7.64.0", // Mantener versiones originales
    "next": "^14.0.0", // Mantener versión original
    "next-auth": "^4.24.5",
    "next-intl": "^3.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "zod": "^3.22.2",
    "zustand": "^4.4.1"
  },
  "devDependencies": {
    "@testing-library/jest-dom": "^6.1.3",
    "@testing-library/react": "^14.0.0",
    "@types/jest": "^29.5.4",
    "@types/node": "^20.6.0",
    "@types/react": "^18.2.21",
    "@typescript-eslint/eslint-plugin": "^6.6.0",
    "cypress": "^13.1.0",
    "eslint": "^8.49.0",
    "eslint-config-next": "^14.0.0", // Mantener versión original
    "eslint-config-prettier": "^9.0.0",
    "husky": "^8.0.3",
    "jest": "^29.6.4",
    "jest-environment-jsdom": "^29.6.4",
    "lint-staged": "^14.0.1",
    "prettier": "^3.0.3",
    "typescript": "^5.2.2"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,css,md}": [
      "prettier --write"
    ]
  }
}

10. Temas Avanzados

Configuración de Monorepo con Turborepo

Configura un monorepo con Turborepo:
// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": ["**/.env.*local"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "lint": {},
    "dev": {
      "cache": false,
      "persistent": true
    },
    "test": {
      "dependsOn": ["build"],
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
    }
  }
}
/apps
  /web                 # Main Next.js application
  /admin               # Admin dashboard
  /docs                # Documentation site
/packages
  /ui                  # Shared UI components
  /utils               # Shared utilities
  /api-client          # API client library
  /config              # Shared configuration
  /tsconfig            # Shared TypeScript configuration

Micro-Frontends

Implementa micro-frontends con Module Federation:
// next.config.js
const { NextFederationPlugin } = require('@module-federation/nextjs-mf');

module.exports = {
  webpack(config, options) {
    const { isServer } = options;
    config.plugins.push(
      new NextFederationPlugin({
        name: 'main',
        remotes: {
          shop: `shop@${process.env.SHOP_URL}/_next/static/${isServer ? 'ssr' : 'chunks'}/remoteEntry.js`,
          blog: `blog@${process.env.BLOG_URL}/_next/static/${isServer ? 'ssr' : 'chunks'}/remoteEntry.js`,
        },
        filename: 'static/chunks/remoteEntry.js',
        exposes: {
          './Header': './components/Header',
          './Footer': './components/Footer',
          './AuthContext': './contexts/AuthContext',
        },
        shared: {
          react: {
            singleton: true,
            requiredVersion: false,
          },
          'react-dom': {
            singleton: true,
            requiredVersion: false,
          },
        },
      })
    );
    // Añadido faltante en original para Module Federation
    if (!isServer) {
       config.output.publicPath = "auto";
    }
    // Fin añadido faltante
    return config;
  },
};

Computación en el Borde (Edge Computing)

Aprovecha la Computación en el Borde para rendimiento global:
// app/api/geo/route.ts
export const runtime = 'edge';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const ip = request.headers.get('x-forwarded-for') || 'unknown';

  // Get user's country from request headers
  const country = request.headers.get('x-vercel-ip-country') || 'unknown';
  const region = request.headers.get('x-vercel-ip-country-region') || 'unknown';
  const city = request.headers.get('x-vercel-ip-city') || 'unknown';

  return Response.json({
    ip,
    geo: {
      country,
      region,
      city,
    },
    timestamp: new Date().toISOString(),
  });
}

Funciones Serverless

Implementa funciones serverless para tareas específicas:
// app/api/generate-thumbnail/route.ts
import { put } from '@vercel/blob';
import { NextResponse } from 'next/server';
import sharp from 'sharp';

export async function POST(request: Request) {
  try {
    const formData = await request.formData();
    const file = formData.get('file') as File;

    if (!file) {
       // Traducido
      return NextResponse.json(
        { error: 'No se proporcionó ningún archivo' },
        { status: 400 }
      );
    }

    // Convert file to buffer
    const buffer = Buffer.from(await file.arrayBuffer());

    // Generate thumbnail
    const thumbnail = await sharp(buffer)
      .resize(200, 200, { fit: 'inside' })
      .toBuffer();

    // Upload to Vercel Blob
    const { url } = await put(`thumbnails/${Date.now()}-${file.name}`, thumbnail, {
      access: 'public',
      contentType: 'image/jpeg', // Asumiendo que sharp genera jpeg por defecto aquí
    });

    return NextResponse.json({ url });
  } catch (error) {
    console.error('Error generating thumbnail:', error);
     // Traducido
    return NextResponse.json(
      { error: 'Fallo al generar la miniatura' },
      { status: 500 }
    );
  }
}

Conclusión

Construir aplicaciones Next.js listas para empresas requiere una cuidadosa consideración de la arquitectura, el rendimiento, la seguridad y la mantenibilidad. Siguiendo las mejores prácticas descritas en esta guía, puedes crear aplicaciones robustas y escalables que satisfagan las demandas de los entornos empresariales. Recuerda que las mejores prácticas pueden evolucionar a medida que Next.js y su ecosistema continúan desarrollándose. Mantente actualizado con los últimos avances y prepárate para adaptar tu enfoque según sea necesario.

Recursos Adicionales