Implementando LiveScale con VTEX IO

Implementando LiveScale com VTEX IO

A pesar de que VTEX ya cuenta con soluciones propias para implementaciones con herramientas externas, es común la necesidad de crear integraciones con partners que aún no están en el ecosistema de la plataforma. Ese fue el caso de LiveScale, el nuevo socio de Motorola en Francia.

En este artículo, te contamos cómo creamos una app que conecta el sistema de listado de productos y carritos VTEX a LiveScale utilizando la estructura IO y su arquitectura de servicios. ¡Compruébalo!

Alineación técnica

Antes de comenzar el desarrollo, es necesario comprender más profundamente todos los requisitos del proyecto. Para ello, realizamos reuniones con el equipo desarrollador de LiveScale, donde resolvimos dudas técnicas y alineamos expectativas.

Además de un Swagger con todas las rutas necesarias y el contenido esperado, también se enviaron ejemplos de implementación en otras plataformas, como SFCC.

Con el tiempo, también realizamos otras reuniones de acompañamiento, asegurando que la tarea avanzaba en la dirección correcta.

Antes de ensuciarse las manos

Para empezar, creamos diagramas de flujo y descripciones de requisitos, con el objetivo de comprender qué builders de VTEX necesitaríamos y cuál sería el flujo lógico de la aplicación.

Como la ejecución tuvo cierta complejidad, fue necesario usar los builders, node (usando servicios y clientes), admin (creando pantallas de configuración en el panel de administración), react (creando la UI que se usará en el admin), mensajes (internacionalización) y graphql (algunas consultas y mutaciones).

Con esas descripciones en mano, enviamos el formulario de creación de la aplicación a VTEX, un proceso necesario para garantizar que tengamos acceso a la aplicación y la libertad de publicarla.

Setup del repositorio y configuración inicial

VTEX proporciona varios boilerplates para crear apps, en nuestro escenario, donde sería necesario consumir algunos servicios en el lado de IO, elegimos usar Service Example. Pero también usamos algunos fragmentos de otros boilerplates, como Admin Example.

Para crear el proyecto, basta utilizar el comando vtex init y luego seleccionar la actividad que tenga más sentido para ti, como se puede ver en esta documentación.

Con nuestro repositorio creado, realizamos la configuración de permisos necesaria en el archivo manifest.json. Allí definimos las políticas para las APIs que se necesitan, como checkout, catalog_system, vbase y graphql.

También realizamos configuraciones para herramientas que optimizan la experiencia del desarrollador, como eslint.

Creando los Clientes

VTEX tiene una arquitectura bien definida, que no solo facilita el desarrollo, sino que también garantiza más rendimiento. Antes de comenzar a desarrollar, fue necesario estudiar y analizar más profundamente las herramientas que utiliza.

Debajo de la implementación de Clients y Services, VTEX utiliza KoaJS, un framework de JavaScript diseñado por el mismo equipo express. El objetivo de esta herramienta es ser más pequeña, más expresiva y crear una base más sólida para crear aplicaciones web y API. VTEX también proporciona una biblioteca que facilita el trabajo con el nodo, llamada node-vtex-api.

Entendiendo la arquitectura de IO

En VTEX IO, los Clients son abstracciones de otros servicios, que pueden ser tanto internos (otros clientes VTEX) como externos. Dado que estaríamos exponiendo información de la tienda a LiveScale, solo necesitamos usar servicios internos. La infraestructura de “programajá” proporciona algunas configuraciones por defecto, tales como:

  • Cache
  • Soporte para métricas nativas
  • Opciones de Retry y timeout


Fuente: https://github.com/vtex-apps/service-example 

Cómo crear y extender los Clients

El SDK @vtex/api ya proporciona una forma estructurada de crear clients. Para ello, es necesario entender qué tipo de comunicación vamos a establecer, como en la siguiente ficha de trabajo:

TipoCaso de uso
AppClientComunicación con otros servicios IO a través de llamadas HTTP
AppGraphQLClientComunicación con otros servicios de IO GraphQL
ExternalClientComunicación con API’s externas
JanusClientComunicación con API’s Core Commerce de VTEX a través del Janus Router
InfraClientComunicación con servicios de infraestructura de VTEX IO
Tabla de Tipos de Comunicación

Después de eso, simplemente extiende el client que tenga más sentido. Para facilitar la visualización, consulta el archivo que VTEX pone a tu disposición para explicar la estructura de un Client. En el ejemplo, este archivo estaría ubicado en node/clients/github.ts.


Fuente: https://developers.vtex.com/docs/guides/vtex-io-documentation-how-to-create-and-use-clients

Siguiendo el ejemplo anterior, implementa sus métodos y, luego, agrega tu Client al servicio que estás creando, siguiendo estos pasos:

1. Crea un archivo index.ts en la ruta node/clients, este se encargará de unificar y abstraer a todos tus clients, además de exponerlos a tus middlewares.

JavaScript

import { IOClients } from '@vtex/api'
import GithubClient from './github.ts'

export class Clients extends IOClients {
  public get status() {
    return this.getOrSet('github', GithubClient)
  }
}

2. Importe a tu Client en la raíz del proyecto de nodo, en node/index.ts.

JavaScript

import type { ClientsConfig, ParamsContext } from "@vtex/api";
import { method, Service } from "@vtex/api";

import { Clients } from "./clients"; 

3. Implementa el client usando ClientsConfig de @vtex/api.

JavaScript

const clients: ClientsConfig<Clients> = {
  implementation: Clients,
  options: {
    default: {
      retries: 20,
      timeout: TIMEOUT_MS
    }
  }
};

4. Ahora, solo usa el cliente en el Servicio exportado.

JavaScript

export default new Service<Clients, State>({
  clients,
  routes: {
    ...
  },
})

5. Usa la siguiente escritura en el archivo index.ts en la carpeta del nodo, que te ayudará a implementar las funciones.

JavaScript

declare global {
  type Context = ServiceContext<Clients, State>
}

6. ¡Listo! Ahora puedes usar tu servicio en middlewares a través del contexto.

JavaScript

export const authorize = async (ctx: Context) {
    const { clients: { github } } = ctx
    ...
    const data = await github.getUser(/*...*/)
}

Siguiendo los pasos, has implementado con éxito los clients. A continuación se muestra un ejemplo de uno de ellos y un método aplicado.

Checkout

OrderForm Devuelve un orderForm dado un orderFormId.

JavaScript

public orderForm = (orderFormId: string) =>
    this.post<OrderForm>(
      this.routes.orderForm(orderFormId),
      { expectedOrderFormSections: ["items"] },
      { metric: "checkout-orderForm" }
    );

Implementando nuestros clients a través de middlewares

Después de extender y crear nuestros clients, es hora de crear middleware para consumirlos y servir su información en los endpoints.

El propósito de usar middleware será implementar métodos que interactúen directamente con los elementos y los integren con el servicio.

Siguiendo el Single-Responsibility Principle, de S.O.L.I.D., separaremos la lógica de cada dominio de cliente en un middleware.

Creación de validators usando middlewares

Para centralizar la lógica de validación, creamos validators, funciones que verifican que la información necesaria ha sido declarada antes de usar los clients. En el siguiente ejemplo, comprobamos si el basketId fue informado.

JavaScript

// Helper para retornar erros de input de usuário
import { UserInputError } from "@vtex/api";

// Função que encapsula lógica de serialização dos parâmetros recebidos
import serializeParams from "../utils/serializeParams";

export default async function validateBasketId(
  ctx: Context,
  next: () => Promise<any>
) {
  const {
    vtex: {
      route: { params }
    }
  } = ctx;

  const { basketId } = params;
	// Caso não exista basketId, é retornado um erro 
  if (!basketId) {
    throw new UserInputError("Basket ID is missing");
  }
	// Caso exista basketId, ele é salvo como state na aplicação.
  ctx.state.basketId = serializeParams(basketId);

  await next();
}

Después de pasar por el validador, se llama al siguiente middleware a través de la función next().

Accediendo a nuestros clients

En seguida, podemos interactuar con nuestros Clients. Para ello, nuestro middleware será una clase, donde cada uno de sus métodos corresponde a un endpoint que se definirá en el futuro. Justo debajo, puedes ver la implementación del middleware Baskets y uno de sus métodos, show, responsable de devolver un carrito, dado un ID.

JavaScript

// Função helper que dado um orderForm (padrão VTEX),
// retorna um basket (padrão LiveShopping)
import convertBasketResponse from "../utils/conveters/convertBasketResponse";

export default class Baskets {
// Métodos
public async show(ctx: Context, next: () => Promise<any>) {
    const {
			// Uso do estado, para recuperar o valor do basketId
      state: { basketId },
			// Uso do nosso Client
      clients: { checkout: checkoutClient },
      host
    } = ctx;
		// Dado um basketId, utilizando nosso client, retornamos um orderForm
    const orderForm = await checkoutClient.orderForm(basketId);
		// Conversão do padrão de orderForm para basket
    ctx.body = convertBasketResponse(orderForm, host);
    await next();
  }
}
}

Reuniéndolo todo en nuestro servicio

Después de crear nuestros clients y el middleware, debemos unir todo a través de un servicio, como se mostró anteriormente.

Creando nuestro servicio

Entonces creamos la ubicación en la raíz de la carpeta del nodo, en el archivo index.ts. Comprueba cómo funciona en los comentarios del código.

JavaScript

// Tipagens da VTEX
import type { ClientsConfig, ParamsContext } from "@vtex/api";

// Classes e funções que iremos utilizar para criar nosso serviço e seus métodos
import { method, Service } from "@vtex/api";

// Nossos Clients
import { Clients } from "./clients";

// Middleware validator
import validateBasketId from "./middlewares/validateBasketId";

// Middleware que encapsula a lógica dos baskets
import Baskets from "./middlewares/baskets";

const TIMEOUT_MS = 800;

// Implementação dos clients e de algumas configurações
const clients: ClientsConfig<Clients> = {
  implementation: Clients,
  options: {
    default: {
      retries: 20,
      timeout: TIMEOUT_MS
    }
  }
};

// Geração da classe Baskets
const baskets = new Baskets();

// Criação do serviço, com declaração do client e das rotas
export default new Service<Clients, State, ParamsContext>({
  clients,
  routes: {
		// O nome de cada filho do objeto routes corresponde a um endpoint,
		// posteriormente declarado no arquivo services.json, na raiz da pasta node
    basketsList: method({
			// Criação da cadeia de execução dos middlewares para cada verbo
			// Nesse exemplo, ao utilizarmos o GET, passamos primeiro para o 
			// validateBasketId, e caso a função next() seja nele passamos para 
			// o método show da classe baskets.
      GET: [validateBasketId, baskets.show]
    }),
  }
});

Exponiendo nuestro servicio a través de endpoints, usando service.json

Después de crear nuestro servicio, es necesario declarar qué endpoints accederán a los elementos secundarios del objeto de rutas, en nuestra clase principal Service.

JavaScript

{
// Configurações gerais
  "memory": 256,
  "ttl": 10,
  "timeout": 2,
  "minReplicas": 2,
  "maxReplicas": 4,
  "workers": 1,
  "routes": {
		// Declaração da rota e atribuição do endpoint
    "basketsList": {
      "path": "/_v/baskets/:basketId",
      "public": false
		}
  }
}

¡Y listo! Este es el flujo utilizado en el desarrollo de aplicaciones siguiendo la arquitectura VTEX.

Espero que este contenido te haya sido de utilidad. Y si quieres recibir otros contenidos como este sobre tecnología, experiencia y marketing para el e-commerce, no olvides suscribirte a nuestra newsletter.

Escrito por:
Miguel Gonçalves, Desenvolvedor Mobile Sênior
at Corebiz