Backend técnico desarrollado en Laravel para una aplicación de gestión de clientes. El módulo permite registrar y listar notas asociadas a un cliente mediante una API REST sencilla.
La solución aplica una estructura DDD básica, separación por capas, CQRS simple para la creación de notas, repositorio definido como contrato en dominio e implementación concreta en infraestructura usando Eloquent.
- PHP 8.4
- Laravel 13
- SQLite
- PHPUnit
- Eloquent ORM
- Composer
POST /api/notesBody:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"client_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
"content": "Primera nota del cliente"
}Respuesta:
201 CreatedSin body.
GET /api/notes?client_id=a1b2c3d4-e5f6-4890-abcd-ef1234567890Respuesta:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"client_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
"content": "Primera nota del cliente",
"created_at": "2026-06-14T09:20:37Z"
}
]Para crear una nota:
| Campo | Reglas |
|---|---|
id |
requerido, string, UUID v4 válido, único |
client_id |
requerido, string, UUID v4 válido |
content |
requerido, string, mínimo 5 caracteres, máximo 1000 |
Para listar notas:
| Campo | Reglas |
|---|---|
client_id |
requerido, string, UUID v4 válido |
Se valida específicamente UUID v4, no únicamente formato UUID genérico.
Ejemplo de UUID v4 válido:
a1b2c3d4-e5f6-4890-abcd-ef1234567890
La solución está organizada en una estructura DDD básica bajo src/:
src/
└── Notes/
├── Domain/
│ ├── Note.php
│ └── NoteRepositoryInterface.php
│
├── Application/
│ ├── Commands/
│ │ └── CreateNoteCommand.php
│ ├── Queries/
│ │ └── ListNotesByClientQuery.php
│ └── Handlers/
│ ├── CreateNoteHandler.php
│ └── ListNotesByClientHandler.php
│
└── Infrastructure/
├── Persistence/
│ └── Eloquent/
│ ├── NoteModel.php
│ └── EloquentNoteRepository.php
└── Providers/
└── NotesServiceProvider.php
Además, la capa HTTP se mantiene en la estructura estándar de Laravel:
app/
└── Http/
├── Controllers/
│ └── Api/
│ └── NoteController.php
└── Requests/
├── StoreNoteRequest.php
└── ListNotesRequest.php
La entidad Note vive en dominio y no depende de Laravel ni de Eloquent.
El objetivo es separar el concepto de negocio de la persistencia.
El contrato NoteRepositoryInterface se define en dominio:
public function save(Note $note): void;
public function findByClientId(string $clientId): array;La implementación concreta se encuentra en infraestructura mediante EloquentNoteRepository.
El modelo NoteModel se utiliza únicamente para persistencia.
Los handlers no usan Eloquent directamente.
La creación de notas se realiza mediante:
CreateNoteCommand
CreateNoteHandler
Para la lectura se ha añadido una query sencilla:
ListNotesByClientQuery
ListNotesByClientHandler
No se ha añadido un bus de comandos ni una librería externa para evitar sobreingeniería en una prueba de alcance reducido.
El binding entre la interfaz del dominio y la implementación de infraestructura se registra en:
src/Notes/Infrastructure/Providers/NotesServiceProvider.php
Y se carga desde:
bootstrap/providers.php
Las respuestas de error de validación en rutas API tienen una estructura uniforme:
{
"message": "Validation failed.",
"errors": {
"field": [
"Error message"
]
}
}Clonar el repositorio:
git clone https://github.com/Tere2087/alira-notes-api.git
cd alira-notes-apiInstalar dependencias PHP:
composer installCrear el archivo de entorno:
cp .env.example .envGenerar clave de aplicación:
php artisan key:generateConfigurar SQLite:
touch database/database.sqliteEn Windows PowerShell:
New-Item database/database.sqlite -ItemType FileComprobar que .env contiene:
DB_CONNECTION=sqliteEjecutar migraciones:
php artisan migrateLevantar el servidor local:
php artisan serveLa API quedará disponible en:
http://127.0.0.1:8000
$body = @{
id = "550e8400-e29b-41d4-a716-446655440000"
client_id = "a1b2c3d4-e5f6-4890-abcd-ef1234567890"
content = "Primera nota del cliente"
} | ConvertTo-Json
Invoke-WebRequest `
-UseBasicParsing `
-Uri "http://127.0.0.1:8000/api/notes" `
-Method POST `
-Headers @{ Accept = "application/json" } `
-ContentType "application/json" `
-Body $bodyRespuesta esperada:
201 Created$response = Invoke-WebRequest `
-UseBasicParsing `
-Uri "http://127.0.0.1:8000/api/notes?client_id=a1b2c3d4-e5f6-4890-abcd-ef1234567890" `
-Method GET `
-Headers @{ Accept = "application/json" }
$response.Contentcurl -i -X POST "http://127.0.0.1:8000/api/notes" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"id":"550e8400-e29b-41d4-a716-446655440000","client_id":"a1b2c3d4-e5f6-4890-abcd-ef1234567890","content":"Primera nota del cliente"}'curl -i "http://127.0.0.1:8000/api/notes?client_id=a1b2c3d4-e5f6-4890-abcd-ef1234567890" \
-H "Accept: application/json"Ejecutar la suite de tests:
php artisan testCasos cubiertos por los tests:
- Crea una nota correctamente.
- Lista notas por
client_id. - No devuelve notas de otros clientes.
- Rechaza
idque no sea UUID v4. - Rechaza
contentcon menos de 5 caracteres. - Rechaza
client_idinválido al listar. - Rechaza identificadores duplicados.
Resultado esperado:
PASS Tests\Feature\NotesApiTest
Tests: 6 passed
POST /api/notes
GET /api/notes?client_id={uuid}
Tiempo real dedicado: aproximadamente 2 horas.
Incluye:
- Preparación del entorno local.
- Creación del proyecto Laravel.
- Diseño de arquitectura DDD básica.
- Implementación de dominio, aplicación e infraestructura.
- Implementación de endpoints REST.
- Validaciones.
- Manejo de errores JSON.
- Tests de API.
- Documentación.