Skip to content
IRC-CodingIRC-Coding
3-Tier ArchitectureDTOService LayerRepository PatternPresentation LayerBusiness LogicData Access

Arquitectura 3-Tier: UI, Business Logic y Data Access

Modelo 3-Tier explicado: capas de presentación, lógica de negocio y acceso a datos. Componentes, ventajas, desventajas y preguntas de examen.

S

schutzgeist

5 min read
Arquitectura 3-Tier: UI, Business Logic y Data Access

type: article

keywords:

  • 3-Schichten-Modell
  • 3-Tier Architektur
  • Präsentation Business Daten
  • DTO

canonicalURL: https://www.irc-coding.de/3-schichten-modell-3-tier-architektur

Arquitectura de software: modelo de 3 capas / 3-Tier

Este artículo es una explicación de conceptos sobre el modelo de 3 capas (3-Tier), incluyendo preguntas de examen, puntos clave y etiquetas.

En resumen

El modelo de 3 capas separa el software lógicamente en presentación, lógica de negocio y acceso a datos para establecer responsabilidades claras, mejorar el mantenimiento y facilitar las pruebas.

Descripción técnica compacta

Las tres capas son:

  1. Presentación (UI)
  2. Business Logic (Services/Use Cases)
  3. Persistence (Repository/ORM/DB)

La comunicación generalmente fluye de arriba hacia abajo. Cada capa solo conoce la que está directamente debajo. Esto mantiene los sistemas débilmente acoplados.

Puntos clave relevantes para exámenes

  • Separación de presentación, lógica y acceso a datos
  • Responsabilidades bien definidas
  • Mejor testabilidad y mantenibilidad
  • Estándar en proyectos Java, .NET y web (relevante para IHK)
  • Componentes intercambiables (cambio de UI)
  • Las capas actúan como barreras de seguridad
  • La modularización reduce costos de mantenimiento
  • La arquitectura debe documentarse

Componentes principales

  1. Capa de presentación
  2. Capa de lógica
  3. Capa de acceso a datos
  4. Interfaces entre capas
  5. Logging y manejo de errores
  6. Unit tests en la capa de negocio
  7. DTOs
  8. Capa de seguridad
  9. Persistencia (SQL/NoSQL)

Ejemplo práctico: gestión de tareas con Python

¿Qué muestra este ejemplo? Un sistema simple de gestión de tareas en Python, construido estrictamente según el modelo de 3 capas. Los datos fluyen desde la presentación (CLI) a través de la capa de negocio (validación, reglas) hasta la capa de persistencia (repositorio SQLite).

¿Por qué es un buen ejemplo para aprender? Cada capa es testeable de forma independiente: la capa de negocio no necesita una base de datos, la presentación no necesita SQLite, se comunican solo a través de llamadas de método limpias.

import sqlite3
from dataclasses import dataclass
from typing import List, Optional

# === Capa 3: Persistence (Acceso a datos) ===
@dataclass
class TaskDTO:
    id: int; title: str; description: str; status: str

class TaskRepository:
    def __init__(self, db_path="tasks.db"):
        self.db_path = db_path
        with sqlite3.connect(db_path) as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS tasks (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    title TEXT NOT NULL, description TEXT,
                    status TEXT DEFAULT 'open')""")

    def create(self, task: TaskDTO) -> TaskDTO:
        with sqlite3.connect(self.db_path) as conn:
            c = conn.execute("INSERT INTO tasks (title,description,status) VALUES (?,?,?)",
                (task.title, task.description, task.status))
            task.id = c.lastrowid
            return task

    def get_all(self) -> List[TaskDTO]:
        with sqlite3.connect(self.db_path) as conn:
            rows = conn.execute("SELECT * FROM tasks").fetchall()
            return [TaskDTO(*r) for r in rows]

    def get_by_id(self, tid: int) -> Optional[TaskDTO]:
        with sqlite3.connect(self.db_path) as conn:
            r = conn.execute("SELECT * FROM tasks WHERE id=?", (tid,)).fetchone()
            return TaskDTO(*r) if r else None

    def update(self, task: TaskDTO) -> TaskDTO:
        with sqlite3.connect(self.db_path) as conn:
            conn.execute("UPDATE tasks SET title=?,description=?,status=? WHERE id=?",
                (task.title, task.description, task.status, task.id))
            return task

    def delete(self, tid: int) -> bool:
        with sqlite3.connect(self.db_path) as conn:
            return conn.execute("DELETE FROM tasks WHERE id=?", (tid,)).rowcount > 0


# === Capa 2: Business Logic (Lógica de aplicación) ===
class TaskService:
    def __init__(self, repo: TaskRepository):
        self._repo = repo

    def create_task(self, title: str, description="") -> TaskDTO:
        if not title or len(title.strip()) == 0:
            raise ValueError("Titel darf nicht leer sein")
        if len(title) > 100:
            raise ValueError("Titel max. 100 Zeichen")
        return self._repo.create(TaskDTO(0, title.strip(), description, "open"))

    def complete_task(self, task_id: int) -> TaskDTO:
        task = self._repo.get_by_id(task_id)
        if not task:
            raise ValueError(f"Task {task_id} nicht gefunden")
        task.status = "done"
        return self._repo.update(task)

    def list_open_tasks(self) -> List[TaskDTO]:
        return [t for t in self._repo.get_all() if t.status != "done"]


# === Capa 1: Presentación (CLI) ===
class TaskCLI:
    def __init__(self, service: TaskService):
        self._service = service

    def run(self):
        while True:
            print("\n1=Neuer Task  2=Offene Tasks  3=Abschließen  4=Beenden")
            choice = input("Wahl: ")
            if choice == "1":
                title = input("Titel: ")
                desc = input("Beschreibung: ")
                try:
                    task = self._service.create_task(title, desc)
                    print(f"Task {task.id} erstellt")
                except ValueError as e:
                    print(f"Fehler: {e}")
            elif choice == "2":
                for t in self._service.list_open_tasks():
                    print(f"[{t.id}] {t.title} ({t.status})")
            elif choice == "3":
                tid = int(input("Task-ID: "))
                try:
                    self._service.complete_task(tid)
                    print("Abgeschlossen")
                except ValueError as e:
                    print(f"Fehler: {e}")
            elif choice == "4":
                break


if __name__ == "__main__":
    repo = TaskRepository()
    service = TaskService(repo)
    cli = TaskCLI(service)
    cli.run()

Ventajas e inconvenientes

Ventajas

  • Aplicación estructurada y mantenible
  • Fácil intercambio de componentes
  • Buena testabilidad

Inconvenientes

  • Mayor esfuerzo inicial
  • Sobrecarga en proyectos muy pequeños

Preguntas típicas de examen (con respuesta breve)

  1. ¿Qué describe el modelo de 3 capas? Presentación, lógica y acceso a datos.
  2. ¿Qué capa valida las entradas? La lógica de negocio.
  3. ¿Qué pertenece a la capa de acceso a datos? Acceso a BD, SQL/ORM, repositorios.

FAQ: preguntas frecuentes sobre el modelo de 3 capas

P: ¿Cuál es la diferencia entre el modelo de 3 capas y la arquitectura 3-Tier? R: El modelo de 3 capas describe una separación lógica (presentación, negocio, persistencia) en la que todas las capas pueden ejecutarse en un servidor. La arquitectura 3-Tier describe una separación física en tres máquinas: cliente, servidor de aplicación y servidor de base de datos. En los exámenes, ambos términos suelen usarse indistintamente.

P: ¿Por qué no acceder directamente a la base de datos desde la UI? R: El acceso directo crea un fuerte acoplamiento. Los cambios en el esquema de la base de datos requieren ajustes en todos los puntos de la UI. Las reglas de negocio no pueden controlarse de forma centralizada. Los ataques de SQL injection son más difíciles de prevenir. La capa de negocio actúa como instancia central de control y protección.

P: ¿Dónde están exactamente los límites entre capas? R: La presentación solo conoce la capa de negocio y pasa datos sin procesar. La capa de negocio valida y pasa DTOs a la capa de persistencia. La persistencia solo conoce SQL/ORM. Si en un repositorio aparecen métodos como “valida” o “verifica”, el límite está violado.

P: ¿Qué es un DTO y por qué se necesita? R: Un DTO (Data Transfer Object) solo transporta datos y no contiene lógica. En el ejemplo de Python, TaskDTO es un DTO. Los DTOs desacoplan las capas, ya que cada una solo necesita conocer el formato DTO, no las estructuras internas de las otras capas.

P: ¿Cuándo no vale la pena el modelo de 3 capas? R: En scripts muy pequeños de pocas cien líneas, la sobrecarga no se justifica. Una vez que se necesitan pruebas unitarias o que la base de datos debe ser intercambiable, la separación tiene sentido. La regla general es: script de una persona = plano, proyecto de equipo o aplicación mantenible = 3 capas.

Respuesta abierta

Para proyectos de IHK, el modelo es ideal porque es fácil de dibujar y justificar. Lo importante es no incluir SQL en los controladores ni acceder a la base de datos desde la UI.

Estrategia de aprendizaje

  1. Dibuja el modelo para un sistema (tienda, blog).
  2. Implementa una app CRUD estrictamente por capas.
  3. Explica las capas en la documentación del proyecto.
  4. Implementa la separación técnicamente a través de paquetes o espacios de nombres.

Información complementaria

  1. https://c4model.com/
Volver al blog
Share:

Entradas relacionadas