Productos del Emprendedor - bootcamp-uchile-2026/grupo-2-backend GitHub Wiki

Productos del Emprendedor

La funcionalidad Productos del Emprendedor permite que los usuarios de BuenOrigen consulten los productos asociados a un emprendedor determinado.

Desde la perspectiva Backend, esta funcionalidad consiste en identificar al emprendedor solicitado, verificar que exista y consultar los productos asociados mediante la relación emprendedorId.

La presentación visual de los productos, tarjetas, filtros, paginación y navegación corresponde a Frontend y Mobile.


Objetivo

Proporcionar a Frontend y Mobile el listado de productos pertenecientes a un emprendedor específico.


Estado

Implementado

La funcionalidad se encuentra implementada mediante:

EmprendedoresModule
        |
        v
EmprendedoresController
        |
        v
EmprendedoresService
        |
        v
ProductosService
        |
        v
ProductoResumenDto[]

Responsabilidad Backend

Actualmente el Backend debe:

  • Recibir el identificador del emprendedor solicitado.
  • Transformar el parámetro emprendedorId a número.
  • Verificar que el emprendedor exista.
  • Consultar los productos asociados al emprendedor.
  • Seleccionar únicamente productos cuyo emprendedorId corresponda al emprendedor solicitado.
  • Transformar los productos a ProductoResumenDto.
  • Informar mediante 404 Not Found cuando el emprendedor no exista.
  • Entregar una lista vacía cuando el emprendedor exista pero no posea productos asociados.

Contrato de API

Endpoint

GET /emprendedores/:emprendedorId/productos

Ejemplo:

GET /emprendedores/2/productos

Parámetro de Ruta

emprendedorId

Tipo esperado:

number

DTO de salida

ProductoResumenDto[]

La definición general del endpoint se encuentra centralizada en:

Endpoints

La estructura del DTO se encuentra documentada en:

DTOs


Parámetro emprendedorId

El Controller recibe el identificador mediante:

@Param('emprendedorId', ParseIntPipe) emprendedorId: number

Los parámetros de una URL llegan originalmente como texto.

Por ejemplo:

GET /emprendedores/2/productos

entrega inicialmente:

"2"

ParseIntPipe lo transforma en:

2

Conceptualmente:

HTTP
"2"
 |
 v
ParseIntPipe
 |
 v
2
 |
 v
EmprendedoresController

Validación del Identificador

Si el parámetro no puede convertirse a un número entero:

GET /emprendedores/abc/productos

ParseIntPipe genera:

400 Bad Request

antes de que la solicitud llegue al Service.

Conceptualmente:

emprendedorId
      |
      v
ParseIntPipe
      |
 +----+----+
 |         |
válido   inválido
 |         |
 v         v
number    400
 |       Bad Request
 v
Service

Verificación del Emprendedor

Antes de buscar los productos, EmprendedoresService comprueba que el emprendedor exista.

Conceptualmente:

const emprendedorEncontrado = this.emprendedores.find(
  (emprendedor) => emprendedor.id === emprendedorId,
);

Si no existe:

if (!emprendedorEncontrado) {
  throw new NotFoundException('Emprendedor no encontrado');
}

Por lo tanto:

emprendedorId
      |
      v
Buscar emprendedor
      |
 +----+----+
 |         |
Existe   No existe
 |         |
 v         v
Buscar    404
productos Not Found

Relación entre Emprendedor y Producto

Un emprendedor puede tener múltiples productos asociados.

Conceptualmente:

Emprendedor
     |
     | 1
     |
     | N
     v
Producto

La relación se representa actualmente mediante:

emprendedorId: number;

dentro del modelo Producto.

Por ejemplo:

{
  id: 10,
  emprendedorId: 2,
  nombre: 'Bolsa Reutilizable'
}

indica que el producto pertenece al emprendedor:

id = 2

Búsqueda de Productos

La búsqueda de productos no se realiza directamente dentro de EmprendedoresService.

Después de verificar que el emprendedor exista, se utiliza:

ProductosService

mediante:

this.productosService.obtenerProductosPorEmprendedor(
  emprendedorId,
);

Conceptualmente:

EmprendedoresService
        |
        | emprendedorId
        v
ProductosService
        |
        | filter()
        v
Producto.emprendedorId === emprendedorId
        |
        v
ProductoResumenDto[]

Separación de Responsabilidades

La responsabilidad de comprobar que el emprendedor exista pertenece actualmente a:

EmprendedoresService

Mientras que la lógica de consultar productos pertenece a:

ProductosService

Conceptualmente:

EmprendedoresService
        |
        | verifica
        v
    Emprendedor
        |
        | solicita productos
        v
ProductosService
        |
        | filtra
        v
    Producto[]

Esto evita duplicar lógica relacionada con productos dentro del dominio Emprendedores.


Comunicación entre Módulos

Para que EmprendedoresService pueda utilizar ProductosService, existe una relación entre ambos módulos.

Conceptualmente:

EmprendedoresModule
        |
        | imports
        v
ProductosModule

ProductosModule exporta:

ProductosService

permitiendo que pueda ser utilizado desde EmprendedoresService.

La dependencia se recibe mediante inyección de dependencias:

constructor(
  private readonly productosService: ProductosService,
) {}

Filtrado por emprendedorId

Dentro de ProductosService, los productos se seleccionan mediante su relación con el emprendedor.

Conceptualmente:

const productosEmprendedor = this.productos.filter(
  (producto) => producto.emprendedorId === emprendedorId,
);

Por ejemplo, si existen:

Producto A → emprendedorId = 1
Producto B → emprendedorId = 2
Producto C → emprendedorId = 2
Producto D → emprendedorId = 3

y se consulta:

GET /emprendedores/2/productos

se seleccionan:

Producto B
Producto C

DTO de Salida

La operación utiliza:

ProductoResumenDto

con la siguiente estructura:

export class ProductoResumenDto {
  id: number;
  marca: string;
  nombre: string;
  precio: number;
  imagenUrl: string;
}

Por lo tanto, la respuesta del endpoint corresponde a:

ProductoResumenDto[]

ProductoResumenDto como DTO Reutilizable

Esta funcionalidad inicialmente consideró la creación de:

ProductoEmprendedorDto

Sin embargo, esa estructura duplicaba información que podía representarse mediante un DTO resumido reutilizable.

Por esta razón, actualmente se utiliza:

ProductoResumenDto

Conceptualmente:

ANTES

Productos del Emprendedor
        |
        v
ProductoEmprendedorDto[]


ACTUALMENTE

Productos del Emprendedor
        |
        v
ProductoResumenDto[]

Esto permite utilizar un contrato genérico para representar productos resumidos sin crear un DTO exclusivamente por el contexto donde aparece.


Modelo vs DTO

El modelo Producto contiene más información que la necesaria para esta operación.

Por ejemplo:

id
subcategoriaId
emprendedorId
marca
nombre
descripcion
precio
precioOferta
imagenUrl
imagenes
stock
puntuacion
ventasUltimoMes
fechaCreacion
colores
tallas

Sin embargo, la respuesta solamente necesita:

id
marca
nombre
precio
imagenUrl

Conceptualmente:

Producto
    |
    | filter por emprendedorId
    v
Productos del Emprendedor
    |
    | map()
    v
ProductoResumenDto[]

Flujo Completo de la Operación

GET /emprendedores/2/productos
              |
              v
        ParseIntPipe
              |
              v
   emprendedorId: number
              |
              v
 EmprendedoresController
              |
              v
  EmprendedoresService
              |
              | find()
              v
   ¿Existe emprendedor?
        |
   +----+----+
   |         |
  Sí         No
   |         |
   v         v
Productos   404
Service    Not Found
   |
   | filter()
   v
Productos con
emprendedorId = 2
   |
   | map()
   v
ProductoResumenDto[]
   |
   v
 200 OK

Ejemplo de Solicitud

GET /emprendedores/2/productos

Ejemplo de Respuesta

[
  {
    "id": 10,
    "marca": "EcoRaíz",
    "nombre": "Bolsa Reutilizable de Algodón",
    "precio": 5990,
    "imagenUrl": "https://ejemplo.cl/imagenes/bolsa-algodon.jpg"
  },
  {
    "id": 15,
    "marca": "EcoRaíz",
    "nombre": "Set de Bolsas para Compras",
    "precio": 8990,
    "imagenUrl": "https://ejemplo.cl/imagenes/set-bolsas.jpg"
  }
]

Respuesta HTTP:

200 OK

Emprendedor sin Productos

Si el emprendedor existe pero ningún producto posee su emprendedorId, el resultado de:

filter()

será:

[]

Por lo tanto, el Backend responde:

[]

con:

200 OK

Esto no se considera un error.

Conceptualmente:

Emprendedor existe
       |
       v
Buscar productos
       |
       v
0 coincidencias
       |
       v
[]
       |
       v
200 OK

Manejo de Errores

Identificador inválido

Ejemplo:

GET /emprendedores/abc/productos

Respuesta:

400 Bad Request

Gestionado por:

ParseIntPipe

Emprendedor inexistente

Ejemplo:

GET /emprendedores/999/productos

Si el emprendedor no existe:

404 Not Found

Ejemplo conceptual:

{
  "statusCode": 404,
  "message": "Emprendedor no encontrado",
  "error": "Not Found"
}

Respuestas HTTP

Código Situación
200 OK Productos obtenidos correctamente
200 OK con [] El emprendedor existe pero no posee productos
400 Bad Request emprendedorId no corresponde a un entero válido
404 Not Found El emprendedor solicitado no existe

Relación con el Perfil de Emprendedor

Desde Perfil de Emprendedor, Frontend y Mobile pueden solicitar los productos asociados mediante:

GET /emprendedores/:emprendedorId/productos

Conceptualmente:

Perfil de Emprendedor
          |
          v
GET /emprendedores/:emprendedorId
          |
          v
EmprendedorDetalleDto

y de manera independiente:

Perfil de Emprendedor
          |
          v
GET /emprendedores/:emprendedorId/productos
          |
          v
ProductoResumenDto[]

De esta forma, el perfil y los productos utilizan contratos independientes.


Documentación Swagger

El endpoint se encuentra documentado mediante:

@ApiOperation()
@ApiParam()
@ApiOkResponse()
@ApiNotFoundResponse()

El parámetro:

emprendedorId

se documenta como:

Number

La respuesta exitosa corresponde a:

ProductoResumenDto[]

y se documenta como arreglo mediante:

@ApiOkResponse({
  type: ProductoResumenDto,
  isArray: true,
})

Para mayor información consultar:

OpenAPI y Swagger


Reglas de Negocio Actuales

Actualmente las reglas pueden resumirse como:

1. emprendedorId debe ser un entero válido.

2. El emprendedor debe existir.

3. Si no existe:
      404 Not Found.

4. Si existe:
      buscar productos mediante ProductosService.

5. Seleccionar productos cuyo:
      producto.emprendedorId === emprendedorId.

6. Transformarlos a:
      ProductoResumenDto[].

7. Si no existen productos:
      retornar [].

Reglas que Actualmente no se Aplican

La implementación actual no filtra todavía productos según:

stock
estadoActivo
estadoPublicacion
fechaPublicacion
disponibilidad

Por lo tanto, actualmente el criterio utilizado es:

producto.emprendedorId === emprendedorId

Posibles Mejoras Futuras

Posteriormente podrán evaluarse:

  • Excluir productos sin stock.
  • Incorporar estado activo o publicado.
  • Incorporar paginación.
  • Ordenar por precio.
  • Ordenar por popularidad.
  • Ordenar por fecha de creación.
  • Incorporar filtros.
  • Permitir búsqueda dentro de los productos del emprendedor.

Estas mejoras deberán incorporarse únicamente cuando sean requeridas por las reglas de negocio de BuenOrigen.


Relación con la Arquitectura

La funcionalidad cruza dos dominios:

Emprendedores
+
Productos

y utiliza:

EmprendedoresModule
        |
        v
EmprendedoresController
        |
        v
EmprendedoresService
        |
        v
ProductosService
        |
        v
ProductoResumenDto[]

Páginas relacionadas: