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:
Tipo | Caso de uso |
AppClient | Comunicación con otros servicios IO a través de llamadas HTTP |
AppGraphQLClient | Comunicación con otros servicios de IO GraphQL |
ExternalClient | Comunicación con API’s externas |
JanusClient | Comunicación con API’s Core Commerce de VTEX a través del Janus Router |
InfraClient | Comunicación con servicios de infraestructura de VTEX IO |
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.