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.
-
Capa de Presentación (Controller): Recibe HTTP. Habla DTOs. Valida entrada.
-
Capa de Negocio (Service): Aplica reglas. Convierte DTOs a Entidades.
-
Capa de Datos (Data/EF Core): Habla SQL. Guarda Entidades.
3. Diccionario de Componentes
| Componente | Rol (Analogía) | Responsabilidad Técnica |
|---|---|---|
| Program.cs | El Arquitecto | Configura la Inyección de Dependencias, conecta la DB y activa el “Airbag” de errores. |
| Controller | El Camarero | Recibe la petición (GET/POST), valida los datos ([Required]) y devuelve códigos HTTP (200, 400, 500). Nunca contiene lógica compleja. |
| DTO | El Plato | Objeto simple (record) para mover datos entre el cliente y el servidor. Protege tu base de datos ocultando campos sensibles. |
| Interface | El Contrato | Define qué métodos debe tener el servicio. Permite crear “Mocks” falsos para los tests. |
| Service | El Chef | Contiene la lógica real. Decide si se puede crear el usuario, transforma datos y llama a la base de datos. |
| Model (Entity) | El Ingrediente | Representa la tabla SQL exacta (Users). Nunca debe salir de la cocina (API) hacia el cliente. |
| DbContext | El Almacén | Es la sesión con la base de datos. Traduce tus objetos C# a comandos SQL. |
| GlobalHandler | Seguridad | Atrapa 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:
-
Entrada: Llega JSON
{"name": "", "email": "mal"}. -
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. -
Si todo va bien: El Controller recibe el DTO limpio.
-
Delegación: El Controller llama a
_userService.CreateUserAsync(dto). -
Transformación: El Service convierte ese
UserDtoa una entidadUser. -
Persistencia: El Service usa
_context.Users.Add()ySaveChangesAsync(). -
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
IInterfacey 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.