Wiki/Fundamentos/Arquitectura Cliente-Servidor
Principiante
15 min lectura

Arquitectura Cliente-Servidor

Fundamentos de la comunicación web y cómo interactúan navegadores con servidores

Modelo Cliente-Servidor

La arquitectura cliente-servidor es un modelo de diseño de software donde las tareas se reparten entre proveedores de recursos o servicios (servidores) y demandantes de servicios (clientes).

Cliente
  • • Navegador web (Chrome, Firefox, Safari)
  • • Aplicación móvil
  • • Aplicación de escritorio
  • • CLI tools

Función: Solicita recursos, muestra información, interactúa con el usuario

Servidor
  • • Servidor web (Node.js, Apache, Nginx)
  • • Servidor de aplicaciones
  • • Servidor de base de datos
  • • Servidor de archivos

Función: Procesa peticiones, ejecuta lógica de negocio, almacena datos

Flujo de Comunicación

┌─────────────┐                          ┌─────────────┐
│   CLIENTE   │                          │  SERVIDOR   │
│  (Browser)  │                          │  (Node.js)  │
└──────┬──────┘                          └──────┬──────┘
       │                                        │
       │  1. HTTP Request                       │
       │  GET /api/users                        │
       ├───────────────────────────────────────>│
       │                                        │
       │                           2. Procesar  │
       │                              petición  │
       │                           3. Consultar │
       │                              DB        │
       │                                        │
       │  4. HTTP Response                      │
       │  200 OK + JSON                         │
       │<───────────────────────────────────────┤
       │                                        │
       │  5. Renderizar datos                   │
       │                                        │

Arquitectura de 3 Capas

1. Capa de Presentación (Frontend)

Interfaz de usuario que muestra datos y captura inputs.

// React Component
function UserList() {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(data => setUsers(data));
  }, []);
  
  return (
    <div>
      {users.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}

2. Capa de Lógica de Negocio (Backend)

Procesa peticiones, aplica reglas de negocio y validaciones.

// Express API Route
app.get('/api/users', authenticate, async (req, res) => {
  // Validar permisos
  if (!req.user.canViewUsers) {
    return res.status(403).json({ error: 'Forbidden' });
  }
  
  // Lógica de negocio
  const users = await userService.getActiveUsers();
  
  // Transformar datos
  const sanitized = users.map(u => ({
    id: u.id,
    name: u.name,
    email: u.email
    // NO incluir password, tokens, etc
  }));
  
  res.json(sanitized);
});

3. Capa de Datos (Database)

Almacena y recupera datos de forma persistente.

// User Service (con Prisma ORM)
class UserService {
  async getActiveUsers() {
    return await prisma.user.findMany({
      where: { 
        isActive: true,
        deletedAt: null
      },
      orderBy: { createdAt: 'desc' },
      take: 100
    });
  }
  
  async createUser(data) {
    // Validaciones
    if (!data.email || !data.password) {
      throw new Error('Email y password requeridos');
    }
    
    // Hash password
    const passwordHash = await bcrypt.hash(data.password, 10);
    
    return await prisma.user.create({
      data: {
        ...data,
        passwordHash,
        isActive: true
      }
    });
  }
}

Implicaciones de Seguridad

⚠️ Nunca confíes en el cliente

El cliente puede ser modificado, inspeccionado y manipulado. TODA validación y seguridad debe implementarse en el servidor.

// ❌ MAL: Validación solo en cliente
// Frontend
if (user.role === 'admin') {
  showDeleteButton(); // El usuario puede modificar esto en DevTools
}

// ✅ BIEN: Validación en servidor
// Backend
app.delete('/api/users/:id', authenticate, (req, res) => {
  if (req.user.role !== 'admin') {
    return res.status(403).json({ error: 'Forbidden' });
  }
  // Proceder con eliminación
});

🔒 Principio de separación

  • • Frontend solo debe manejar UI/UX
  • • Backend maneja autenticación, autorización y lógica crítica
  • • Base de datos solo accesible desde backend (nunca directamente desde cliente)
  • • Secrets y API keys NUNCA en código frontend

Siguiente Paso

Aprende sobre HTTP, el protocolo que hace posible esta comunicación

HTTP: El Protocolo de la Web