1. Estructura Física (Carpetas)

Así es como organizamos los archivos para separar responsabilidades. Fíjate que el código de producción (src) está separado de las pruebas (tests).

/UserApi (Solución .sln)
│
├── /src (Código Real)
│   └── /UserApi.Web
│       ├── /Controllers      # 📡 Puerta de entrada (Endpoints HTTP)
│       │   └── UsersController.cs
│       │
│       ├── /DTOs             # 📦 Cajas de transporte (Seguridad/Validación)
│       │   └── UserDto.cs (record)
│       │
│       ├── /Interfaces       # 📜 Contratos (Qué se hace, no cómo)
│       │   └── IUserService.cs
│       │
│       ├── /Services         # 🧠 Lógica de Negocio (El cerebro)
│       │   └── UserService.cs
│       │
│       ├── /Models           # 🗃️ Entidades (Tablas de DB pura)
│       │   └── User.cs
│       │
│       ├── /Data             # 🔌 Conexión a Base de Datos
│       │   └── AppDbContext.cs
│       │
│       ├── /Infrastructure   # 🛡️ Fontanería técnica (Middelware)
│       │   └── GlobalExceptionHandler.cs
│       │
│       └── Program.cs        # ⚙️ Configuración y DI (El inicio de todo)
│
└── /tests (Aseguramiento de Calidad)
    └── /UserApi.UnitTests
        └── UsersControllerTests.cs

2. Estructura Lógica (El Flujo de los Datos)

Cuando un usuario envía una petición, los datos viajan a través de estas capas. Cada capa solo “conoce” a la que tiene justo debajo.

  1. Capa de Presentación (Controller): Recibe HTTP. Habla DTOs. Valida entrada.

  2. Capa de Negocio (Service): Aplica reglas. Convierte DTOs a Entidades.

  3. Capa de Datos (Data/EF Core): Habla SQL. Guarda Entidades.


3. Diccionario de Componentes

ComponenteRol (Analogía)Responsabilidad Técnica
Program.csEl ArquitectoConfigura la Inyección de Dependencias, conecta la DB y activa el “Airbag” de errores.
ControllerEl CamareroRecibe la petición (GET/POST), valida los datos ([Required]) y devuelve códigos HTTP (200, 400, 500). Nunca contiene lógica compleja.
DTOEl PlatoObjeto simple (record) para mover datos entre el cliente y el servidor. Protege tu base de datos ocultando campos sensibles.
InterfaceEl ContratoDefine qué métodos debe tener el servicio. Permite crear “Mocks” falsos para los tests.
ServiceEl ChefContiene la lógica real. Decide si se puede crear el usuario, transforma datos y llama a la base de datos.
Model (Entity)El IngredienteRepresenta la tabla SQL exacta (Users). Nunca debe salir de la cocina (API) hacia el cliente.
DbContextEl AlmacénEs la sesión con la base de datos. Traduce tus objetos C# a comandos SQL.
GlobalHandlerSeguridadAtrapa cualquier explosión (Excepción), la registra en el log y le dice al cliente “Error interno” sin dar detalles.

4. El Ciclo de Vida de una Petición (POST /users)

Para que veas cómo se conectan las piezas en movimiento:

  1. Entrada: Llega JSON {"name": "", "email": "mal"}.

  2. Validación: El sistema ve los atributos en UserDto ([Required]). Como están mal, rechaza la entrada (400 Bad Request). El Controller ni se entera.

  3. Si todo va bien: El Controller recibe el DTO limpio.

  4. Delegación: El Controller llama a _userService.CreateUserAsync(dto).

  5. Transformación: El Service convierte ese UserDto a una entidad User.

  6. Persistencia: El Service usa _context.Users.Add() y SaveChangesAsync().

  7. Salida: Todo vuelve hacia atrás y el Controller responde 201 Created.

5. ¿Por qué es mejor que tu PHP Legacy?

  • Desacoplado: Si cambias MySQL por SQL Server, solo tocas Program.cs.

  • Testable: Como el Controller depende de IInterface y no de la clase concreta, puedes mentirle en los tests.

  • Seguro: No expones la estructura de tu DB gracias a los DTOs y al Exception Handler.

    entity_framework_core