Skip to content

Repository files navigation

📦 Shipment-System — API de Gestión de Envíos

API REST en NestJS para registrar envíos, calcular la tarifa según la distancia y consultar el histórico. Construida con arquitectura hexagonal (dominio desacoplado de la infraestructura) y empaquetada con Docker.

CI NestJS Node TypeScript License

Proyecto originalmente realizado como prueba técnica y refactorizado para compartir buenas prácticas.


✨ Funcionalidades

  • Registrar envíos y calcular la tarifa automáticamente según la distancia.
  • Consultar el histórico completo de envíos.
  • Validación de entrada con class-validator (vía ValidationPipe global).
  • Arquitectura hexagonal: el dominio no depende de NestJS ni de la persistencia.
  • Persistencia intercambiable detrás del puerto ShipmentRepository.

🧱 Arquitectura

src/
├── domain/
│   ├── entities/shipment.entity.ts              # Entidad de dominio
│   └── repositories/shipment.repository.interface.ts  # Puerto (interface)
├── usecase/shipment/
│   ├── create-shipment.usecase.ts               # Crea y calcula la tarifa
│   └── list-shipments.usecase.ts                # Lista los envíos
├── dao/shipment.dao.ts                          # Adaptador in-memory del puerto
├── view/
│   ├── controller/shipment.controller.ts        # Endpoints REST
│   ├── form-request/create-shipment.request.ts  # DTO de entrada (validado)
│   └── dtos/shipment.dto.ts                      # DTO de salida
└── main.ts                                       # Bootstrap + ValidationPipe

La inyección del repositorio se hace por token ('ShipmentRepository' → ShipmentDao), así los casos de uso dependen de la interfaz, no de la implementación. Para usar PostgreSQL/Mongo basta con implementar ShipmentRepository y cambiar el binding en app.module.ts.


🔗 Endpoints

Método Endpoint Descripción
POST /shipments Registrar un nuevo envío
GET /shipments Listar todos los envíos

POST /shipments

{
  "recipient": "Juan Pérez",
  "sender": "Acme Inc.",
  "content": "Libro",
  "distance": 20
}

Respuesta 201

{
  "id": "b1f0c2a4-3d6e-4f8a-9c1b-2e5d7a8f0c3e",
  "recipient": "Juan Pérez",
  "sender": "Acme Inc.",
  "content": "Libro",
  "shipmentDate": "2024-04-18T16:40:31.160Z",
  "distance": 20,
  "fee": 15
}

Un payload inválido (campo faltante o propiedad no permitida) responde 400.


🧮 Cálculo de la tarifa

La tarifa es lineal: una base fija más un costo por kilómetro.

fee = 5 + 0.5 × distancia
Distancia (km) Tarifa
0 5
10 10
20 15
100 55

La lógica vive en create-shipment.usecase.ts y está cubierta por tests.


🚀 Cómo correrlo

Docker

docker build -t shipment-system .
docker run -d -p 3000:3000 --name shipment-system shipment-system
curl http://localhost:3000/shipments   # []

O con Docker Compose:

docker compose up -d

Local

npm install
npm run start:dev        # http://localhost:3000

🧪 Tests

npm run test       # unit (jest)
npm run test:cov   # con cobertura
npm run test:e2e   # end-to-end (supertest)

Cubren el cálculo de la tarifa, los casos de uso, el DAO y el controller. La suite corre automáticamente en GitHub Actions (.github/workflows/ci.yml) junto con el lint y el build.


🗄️ Stack

NestJS 10 · TypeScript 5 · Node 20 · class-validator · Jest · Supertest · Docker


📄 Licencia

MIT — código de propiedad exclusiva de Alejandro Vega.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages