No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Aprende todo un fin de semana sin pagar una suscripci贸n 馃敟

Aprende todo un fin de semana sin pagar una suscripci贸n 馃敟

Reg铆strate

Comienza en:

3D
20H
36M
15S

Introducci贸n a servicios: crea tu primer servicio

14/27
Recursos

Aportes 46

Preguntas 15

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Les dejo mis apuntes de la clase 馃檲

As铆 entend铆 鈥渃lean arquitecture鈥.

Me encantar铆a leer correcciones.

Uff hasta ahora el curso me ha encantado, super claro y bien estructurado. Asi va mi codigo por ahora, me ha parecido muy buena la parte de encapsular el funcionamiento en clases 馃槃

Products.routes

const express = require('express');
const router = express.Router();
const productService = require('../services/product.service');
const product = new productService();

router.get('/', (req, res) => {
    let {limit, offset} = req.query;
    res.json(product.find({offset, limit}));
});

router.get('/:id', (req, res) => {
    const {id} = req.params;
    res.json(product.findOne(id));
});


router.post("/", (req, res) => {
    const {name, price, image} = req.body;

    const newProduct = product.create({name, price, image});
    if (newProduct) {
        res.status(201).json({
            message: "Product added",
            data: newProduct
        });

    } else
        res.status(501).json({message: "internal error"});
});


router.patch("/:id", ((req, res) => {
    const id = parseInt(req.params.id);
    const {name, price, image} = req.body;
    const updateProduct = product.update(id, {name, price, image});
    if (updateProduct) {
        res.json({
            message: "Product updated",
            data: req.body
        });
    } else {
        res.status(501).json({message: "Internal error"})
    }


}));

router.delete("/:id", (req, res) => {
    const id = parseInt(req.params.id);
    const currentProduct = product.delete(id);
    if (currentProduct) {
        res.status(201).json({message: "Product deleted", data: currentProduct});
    } else {
        res.status(404).json({message: "Product not found"});
    }
});


module.exports = router;

prduct.service.js

const faker = require("faker");

class ProductService {

    constructor() {
        this.products = [];
        this.faker();
    }

    faker(quantity = 100) {
        for (let i = 0; i < quantity; i++) {
            this.products.push({
                id: i + 1,
                name: faker.commerce.productName(),
                price: parseInt(faker.commerce.price(), 10),
                image: faker.image.imageUrl()
            });
        }

    }

    create(product = {}) {
        const newProduct = {
            id: this.products[this.products.length - 1].id + 1,
            name: product.name,
            price: parseInt(product.price),
            image: product.image
        }
        this.products.push(newProduct);

        return newProduct;
    }

    find(filter = {}) {
        return this.products.slice(filter.offset | 0, filter.limit ? parseInt(filter.limit) + parseInt((filter.offset | 0)) : undefined)
    }


    findOne(id) {
        return this.products.find((p) => p.id = id);
    }

    update(id, changes = {}) {
        const product = this.products.find((prod) => prod.id === id);
        const idx = this.products.findIndex((prod) => prod.id === id);
        if (product) {
            this.products[idx] = {
                id: product.id,
                name: changes.name || product.name,
                price: changes.price || product.price,
                image: changes.image || product.image
            };
            return product;
        }
    }


    delete(id) {
        const product = {...this.products.find((prod) => prod)};
        if (product) {
            this.products = this.products.filter((p) => p.id !== id);
            return product;
        }
    }
}

module.exports = ProductService;

Resumen de la clase:
Concepto
Los servicios es donde encapsulamos todos los casos de usos y comenzar a interactuar con la l贸gica de negocio.
En el caso de una tienda: hacer compras, transacciones, etc.
.
Estructura
Esta arquitectura est谩 definida por capas.
Entidades:

  • En esta capa encontramos las entidades base del negocio.
  • En nuestro caso: productos, categor铆as, 贸rdenes de compra.

Casos de uso

  • En esta capa tenemos lo relacionado a la l贸gica de negocio
  • En esta capa se encuentra los servicios

Controladores

  • En esta capa se brinda el acceso.
  • Aqu铆 encontramos el routing

.

Flujo de trabajo:

  • Controladores: Encontramos los routes y middlewares.
  • Los controladores acceden a la capa de servicios
  • Servicios: donde se encuentra la l贸gica de negocio
  • Los servicios usan las librer铆as.
  • Las librer铆as se encargan de contactarse a la capa de entidades
  • Las librer铆as se contactan a otras fuentes de datos: API externa o base de datos.

Me est谩 encantando este curso, c贸mo es que lo explica el profesor.
Incluso me estoy interesando mucho por el mundo del backend. 馃榿

Este profesor es muy bueno, el tema de los servicios siempre me hab铆an confundido cundo los us谩bamos en otros cursos de la Escuela de JS.

Para la peticion get que busca por id una manera de validar primero si hay algun valor retornado se puede hacer de la siguiente manera:

router.get('/:id', (req, res) => {
  const { id } = req.params;

  const product = service.findOne(id);

  if(!product){
    res.status(404).json({
      message: `El producto con id ${id} no existe`
    });
  } else {
    res.status(200).json({
      product
    });
  }

});

De esta manera nos aseguramos que traiga valor la consulta de lo contraria manejamos la respuesta que no encontr贸 el objeto buscado.

Hasta este punto me siento super bien, con la forma de explicar de este profe, grande Nico!

De una vez agregue una validaci贸n si no encuentra el id para retornar un 404 (yo lo estoy haciendo con books en vez de products):

router.get('/:id', (req, res) => {

    const { id } = req.params;
    const book = service.findOne(id);

    if ( book ) {
        res.status(200).json({
            message: "Libro encontrado",
            book        
        })
    } else {
        res.status(404).json({
            message: "Libro no encontrado :( ",
        })
    }
}) 

Les comparto mi c贸digo con mejoras para realizar un limit

馃憖 OJO:
Ya que el profesor menciona mucho el tema de las convenciones a la hora de nombrar las cosas, aqui les dejo esta convenci贸n:

  • Las clases se nombran en singular y usando PascalCase:
    • Ejemplo: Persona, Producto, Categoria, o en en el caso de esta clase deber铆a ser ProductService.
  • Las funciones se nombran en verbos (crear, agregar, actualizar, etc) y usando camelCase:
    • Ejemplo: crearProducto(), agregarCategoria(), eliminarProducto()

Tomen nota 鉁嶏笍馃槈

Felicitationes Nicolas, excelente profesor.

Profe que se me hace que ese sweater se le va a perder 馃憖鈥

mas profesores as铆 en Platzi.

Servicios Web
Nacen de la necesidad de cubrir la l贸gica de negocio.
L贸gica de negocio
El t茅rmino l贸gica de negocio hace referencia a la parte de un sistema que se encarga de codificar las reglas de negocio del mundo real que determinan c贸mo la informaci贸n puede ser creada, almacenada y cambiada.

  • En el 谩mbito de la programaci贸n, es una de las capas del modelo MVC Modelo Vista-Controlador separando as铆 la complejidad del desarrollo en capas independientes.

Servicios
Los servicios es donde encapsulamos todos los casos de usos y comenzamos a interactuar con la l贸gica de negocio.

  • En el caso de una tienda: hacer compras, transacciones, etc.

Los servicios deben deben ser construidos por una arquitectura, que en este caso ser谩:
Clean Architecture
Clean architecture es un conjunto de principios cuya finalidad principal es ocultar los detalles de implementaci贸n a la l贸gica de dominio de la aplicaci贸n.

De esta manera mantenemos aislada la l贸gica, consiguiendo tener una l贸gica mucho m谩s mantenible y escalable en el tiempo.


Capas
-Entidades:

En esta capa encontramos las entidades base del negocio.
En nuestro caso: productos, categor铆as, 贸rdenes de compra.
-Casos de uso

En esta capa tenemos lo relacionado a la l贸gica de negocio
En esta capa se encuentra los servicios
-Controladores

En esta capa se brinda el acceso.
Aqu铆 encontramos el routing.

Flujo de trabajo:

  • Controladores: Encontramos los routes y middlewares.
  • Los controladores acceden a la capa de servicios
  • Servicios: donde se encuentra la l贸gica de negocio
  • Los servicios usan las librer铆as.
  • Las librer铆as se encargan de contactarse a la capa de entidades
  • Las librer铆as se contactan a otras fuentes de datos: API externa o base de datos.

Implementando el query limit a products.
.
products.service

const faker = require('faker');

class ProductsService {
  constructor() {
    this.products = [];

    const limit = 1000;
    this.generate(limit);
  }

  generate(limit) {
    for (let index = 0; index < limit; index++) {
      this.products.push({
        id: faker.datatype.uuid(),
        name: faker.commerce.productName(),
        price: parseInt(faker.commerce.price(), 10),
        image: faker.image.imageUrl(),
      });
    }
  }

  create() {}

  find(limit) {
    return this.products.slice(0, limit);
  }

  findOne(id) {
    return this.products.find((item) => item.id === id);
  }

  update() {}
  delete() {}
}

module.exports = ProductsService;


products.router

const express = require('express');

const ProductsService = require('./../services/products.service');

const router = express.Router();
const service = new ProductsService();

router.get('/', (req, res) => {
  const { limit } = req.query;
  const products = service.find(limit || 10);
  res.json({
    size: products.length,
    products,
  });
});

router.get('/filter', (req, res) => {
  res.send('Yo soy un filter');
});

router.get('/:id', (req, res) => {
  const { id } = req.params;
  const product = service.findOne(id);
  if (!product) {
    res.status(404).json({
      message: `El producto con id '${id}' no existe`,
    });
  } else {
    res.status(200).json(product);
  }
});

.
Sin limit

Con limit

APORTE

si no quieres ver en tus require lo siguiente

const example = require('./../services/product.service')

puedes crear un archivo llamado jsconfig.json en la raiz de tu proyecto con el siguiente codigo.

{
 "compilerOptions": {
   "baseUrl": "root_project",
 }
}

donde root_project sera el path de donde quieres llamar a todos tus archivos, ejemplo quiero que todos mis archivos sean llamados desde mi raiz, podemos colocar la siguiente configuracion.

{
 "compilerOptions": {
   "baseUrl": ".",
 }
}

Al estar el archivo jsconfig.json en la raiz el punto hace referencia a si mismo, pudiendo entonces requerir nuestro archivo service de ejemplo de la siguiente forma.

const example = require('services/product.service')

Evitar los import relativos nos puede ayudar a comprender mejor donde estan nuestros archivos requeridos,

Recuerden que si quieren usar Faker sin ning煤n inconveniente, deben descargarlo como

npm i [email protected]5.5.3

Especificando la versi贸n, porque la 6.6.6 no funciona
Fecha: 13-2-2022

8:59

Un string super largo que va a actuar de forma randomnica

馃槶馃ぃ

Excelente, ahora entiendo cu谩ndo se refieren a servicios.

Aun no termino el curso y ya estoy maravillado de este curso, no entiendo como no lanzaron antes este curso, buenisimo el profesor y el contenido.

muy genial todo.

Ac谩 les dejo el generador de usuarios con algunos campos importantes, espero les sirva 馃槃

generate(){
    const limit = 20;
    for(let i=0;i<limit;i++){
      this.users.push({
        id: faker.datatype.uuid(),
        name: faker.name.firstName(),
        lastName: faker.name.lastName(),
        avatar: faker.image.imageUrl(),
        city: faker.address.cityName(),
        email: faker.internet.email(),
      })
    }
  }

Excelente profesor. Muy bien explicado todo el curso hasta ahora un tema que en un principio me hab铆a resultado super complicado.

Les comparto mi c贸digo con mejoras para realizar un limit


class ProductsService{

  constructor(){
    this.products = [];
    this.generate();
  }

  generate(limit){
    console.log(limit)
    for(let index = 0; index<(limit||1000); index++){
    this.products.push({
      id:faker.datatype.uuid(),
      name:faker.commerce.productName(),
      price: parseInt(faker.commerce.price(),10),
      image:faker.image.imageUrl()

    });
    }
  }
  find(limit){
    return this.products.slice(0,limit)
  }
}

module.exports = ProductsService;```

```  const express  = require('express');
const  ProductsService = require('./../services/product.service')
const router = express.Router();
const service = new ProductsService();

router.get('/',(req,res)=>{
  let {limit,page} =req.query;
  limit = limit || 10;
  const products = service.find(limit);
  res.json({
    size:products.length,
    products})
})


module.exports = router;

Me gusta mucho c贸mo queda el c贸digo despu茅s de aplicar clean arquitecture 馃憦馃徏

Mi soluci贸n antes de ver el siguiente v铆deo:

products router

const express = require('express');
const router = express.Router();

const ProductsService = require('../services/product.service')
const service = new ProductsService();

router.get('/', (req, res) => {
  const products = service.findAll()
  res.json(products)
})

//! Rutas espec铆ficas ANTES que rutas din谩micas

router.get('/:id', (req, res) => {
  const { id } = req.params; //* tiene que ser igual que en los query params
  const product = service.findOne(id)
  res.json(product)
})

router.post('/', (req, res) => {
  const body = req.body
  const { name, price, image } = body
  service.create(name, price, image)

  res.status(201).json({
    message: 'product created',
    data: body
  })
})

router.patch('/:id', (req, res) => {
  const { id } = req.params
  const body = req.body
  const { name, price, image } = body
  service.update(id, name, price, image)

  res.json({
    message: 'updated partial',
    data: body,
    id
  })
})

router.delete('/:id', (req, res) => {
  const { id } = req.params
  service.delete(id)

  res.json({
    message: 'deleted',
    id
  })
})

module.exports = router;

products service:

const { faker } = require('@faker-js/faker')

class ProductsService {

  constructor() {
    this.products = []
    this.generate() //* Crear谩 los productos iniciales
  }

  generate() {
    const limit = 100

    for(let index = 0; index < limit; index++) {
      this.products.push({
        id: faker.datatype.uuid(),
        name: faker.commerce.productName(),
        price: parseInt(faker.commerce.price(), 10),
        image: faker.image.imageUrl(),
      })
    }
  }

  create(name, price, image) {
    this.products.push({
      id: faker.datatype.uuid(),
      name,
      price,
      image
    })
  }

  findAll() {
    return this.products
  }

  findOne(id) {
    return this.products.find(item => item.id === id)
  }

  update(id, name, price, image) {
    const product = this.findOne(id)

    name !== undefined ? product.name = name : null
    price !== undefined ? product.price = price : null
    image !== undefined ? product.image = image : null

    return product
  }

  delete(id) {
    const product = this.findOne(id)
    this.products.splice(product, 1)
  }
}

module.exports = ProductsService

Esto va excelente 馃槃

<
router.get('/:id', (req,res) => {
  const {id} = req.params;
  const product = service.findOne(id);
  if(product === undefined){
    res.status(404).json({
      message:'Product not found, please check your id'
    });
  }else{
    res.status(200).json(product);
  }

});
> 

yo le a帽ad铆 validaci贸n al m茅todo findOne en caso de no encontrar el producto con el id recibido en los par谩metros

findOne(id){
        const find = this.products.find(el => el.id == id)
        if (find){
            return find
        }

        return {
            message: 'not found'
        }
    }
class ProductsServices {
    constructor(){
        this.products = []
        this.generate()
    }

    generate(){
        for (let i = 0; i < 10; i++){
            this.products.push({
                id: faker.datatype.uuid(),
                name: faker.commerce.productName(),
                price: faker.commerce.price(),
                image: faker.image.imageUrl()
            })
        }
    }

    find(){
        return this.products
    }

    findOne(id){
        const find = this.products.find(el => el.id == id)
        if (find){
            return find
        }

        return {
            message: 'not found'
        }
    }
}

Yo lo hice con query params

router.get('/', (req, res)=>{
  const id  = req.query.id || null;
  if(id === null){
    const products = service.find();
    res.json(products)
  }
  else{
    const products = service.findOne(id);
    res.json(products);
  }
})

Existe una extensi贸n en visual llamada Thunder, para hacer peticiones, no hace falta abrir otra cosa.

const { faker } = require("@faker-js/faker");

class ProductsService {

  constructor(){
    this.products = [];
    this.generate();
  }

  generate(){
    const limit = 100;
    for (let i = 0; i < limit; i++) {
      this.products.push({
        id: faker.datatype.uuid(),
        name: faker.name.fullName(),
        price: parseInt(faker.commerce.price(), 10),
        image: faker.image.imageUrl()
      });
    }
  }

  create(){

  }

  find(){
    return this.products;
  }

  findOne(id){
    return this.products.find(item => item.id === id);
  }

  update(){

  }

  delete(){

  }

}

module.exports = ProductsService;

No olviden colocar el new cuando instancian service. Estuve 30 minutos buscando el error, hasta que tuve que chequear el cod del profesor.
Y si se olvidan que se instancia con new, no se olviden de leer la terminal; estaba tan ansioso en buscar el error que identificaba el donde, y no vi que literalmente la terminal me decia:

const service = ProductService();
                ^

TypeError: Class constructor ProductService cannot be invoked without 'new'   

鈥淐ontrollers鈥 == 鈥淩outes鈥

Me conflictua bastante ver POO en JS , cuando JS no es un lenguaje orientado a objetos, si no funcional. me gustaria debatir esto y de verdad JS no POO o OOP

Tengo una gran duda, si es que pongo este c贸digo:

generate() {

    let limit = 20

    while(limit > 0) {

      //Usamos los m茅todos de faker el cual nos genera datos aleatorios de un ecommerce
        this.products.push({

        id: parseInt(Math.random() * 23 ).toString() || faker.datatype.uuid(),
        name: faker.commerce.productName(),
        price: faker.commerce.price(),
        url: faker.image.imageUrl()
      })

      limit--
    }
  }

Esta todo bien, pero si le cambio el nombre a las keys por ejemplo por estas

generate() {

    let limit = 20

    while(limit > 0) {

      //Usamos los m茅todos de faker el cual nos genera datos aleatorios de un ecommerce
        this.products.push({

        id: parseInt(Math.random() * 23 ).toString() || faker.datatype.uuid(),
        productName: faker.commerce.productName(),
        price: faker.commerce.price(),
        productURL: faker.image.imageUrl()
      })

      limit--
    }

No me crea los registros, alguna idea?

El c贸digo qued贸 medio raro porque use mucho la palabra 鈥渟ervice鈥 馃槀, pero ya lo hab铆a definido as铆

class ServicesService {

  constructor(){
    this.services = [
      {
        id: '1',
        service: "Dise帽o de Cejas",
        category: "Cejas",
        price: 7000,
        durationMin: 5
      },
      {
        id: '2',
        service: "Pigmentaci贸n de Cejas",
        category: "Cejas",
        price: 5000,
        durationMin: 15
      },
      {
        id: '3',
        service: "Corte de cabello",
        category: "Cabello",
        price: 20000,
        durationMin: 30
      },
      {
        id: '4',
        service: "Secado de cabello",
        category: "Cabello",
        price: 12000,
        durationMin: 30
      }
    ];

  }

  create() {};
  find() {
    return this.services;
  };
  findOne(id) {
    return this.services.find(item => item.id === id );
  };
  update() {};
  delete() {};
};

module.exports = ServicesService;

Para el caso de la consulta de producto:
Recibo el id, instancio la clase y hago una validacion, en caso de encontrar el producto por el id que se envio retorno un status 200 mas el producto.
Caso constrario retorno un status 401 y un mensaje.

router.get('/:id', (req, res) => {
  const { id } = req.params
  const product = service.findOne(id)
  if(product) res.status(200).json(product)
  else res.status(401).json({message:"not found"})
})

Generador para Categorias

  generate() {
    const limit = 10;

    for (let i = 0; i < limit; i++) {
      this.categories.push({
        id: faker.datatype.uuid(),
        nombre: faker.commerce.productMaterial()
      });
    }
  }

Una validaci贸n simple para cuando se requiera un producto inexistente:

router.get('/:id', (req, res) => {
  const { id } = req.params;
  const product = service.findOne(id);
  if (product) {
    res.status(200).json(product);
  } else {
    res.status(404).json({ message: 'Product not found' });
  }
});

Creo que ten铆a una muy mala pr谩ctica y es que combinaba la capa de servicios con los controladores.

  • Clean Arquitecture: Entidades, Casos de Uso (Logica de Negocio), Controladores (Brindan acceso [Routing, Middleware])

Controladores -> Services -> Libs

Esta organizaci贸n de carpetas y archivos hace hermosa la aplicacion

Muy buena explicaci贸n del funcionamiento de los servicios.