Centralización de Validaciones con un Esquema GraphQL como Single Source of Truth (SSOT)

Resumen

Este trabajo aborda la necesidad de centralizar las reglas de validación de datos en un sistema que utiliza tanto TypeScript como Go. Tradicionalmente, las validaciones se implementan en diferentes capas de una aplicación, lo que puede conducir a redundancias y errores de mantenimiento. Para resolver este problema, proponemos utilizar un esquema GraphQL como Single Source of Truth (SSOT) para definir y centralizar las reglas de validación. Hemos desarrollado un generador de código que toma las definiciones del esquema .gql y genera automáticamente validaciones para TypeScript, utilizando Yup, y para Go, utilizando GoValidator.


Introducción

En sistemas donde se utilizan múltiples lenguajes de programación y diferentes tecnologías para la validación de datos, puede ser un desafío mantener la coherencia de las reglas de validación. Cada lenguaje tiene sus propias librerías y convenciones, lo que puede dar lugar a inconsistencias entre las validaciones en el frontend y el backend. Para abordar este problema, adoptamos el esquema GraphQL como un SSOT (Single Source of Truth) para centralizar las reglas de validación.

GraphQL es ideal para este propósito porque permite definir los tipos de datos, junto con las restricciones de validación, de manera clara y estructurada. Al utilizar directivas personalizadas en el esquema GraphQL, podemos especificar reglas de validación que luego se aplican automáticamente en todas las partes del sistema que consumen ese esquema. Esto asegura que las validaciones sean consistentes en todas las capas de la aplicación, independientemente de la tecnología o el lenguaje utilizado.

Metodología

1. Uso de GraphQL como SSOT

El primer paso en nuestra solución es definir las reglas de validación directamente en el esquema GraphQL. En lugar de escribir validaciones por separado en el frontend y backend, centralizamos todas las validaciones en un solo lugar utilizando directivas personalizadas en el esquema. Las directivas permiten definir restricciones como la longitud mínima, longitud máxima, patrones de expresión regular y validación de campos requeridos.

Ejemplo de Esquema GraphQL:

"""
Representa un usuario en el sistema con validaciones centralizadas.
"""
type User {
id: ID!
fullName: String! @stringConstraint(minLength: 3, maxLength: 50, pattern: "^[a-zA-Z ]+$")
email: String! @emailConstraint(message: "Please enter a valid email")
mobilePhone: String! @stringConstraint(pattern: "^[0-9]{7,15}$", required: true)
mobileCodePhone: String! @stringConstraint(pattern: "^[0-9]{1,4}$", required: true)
timezone: String! @stringConstraint(required: true)
password: String! @stringConstraint(minLength: 8, required: true)
status: UserStatus!
createdAt: Time!
updatedAt: Time!
}

# Inputs para registrar y actualizar usuarios
input RegisterUserInput {
fullName: String! # Reutiliza las validaciones de `User`
email: String! # Reutiliza las validaciones de `User`
mobileCodePhone: String! # Reutiliza las validaciones de `User`
mobilePhone: String! # Reutiliza las validaciones de `User`
timezone: String! # Reutiliza las validaciones de `User`
password: String! # Reutiliza las validaciones de `User`
confirmPassword: String! @stringConstraint(minLength: 8, required: true) # Validación específica
}

input UpdateUserInput {
fullName: String # Reutiliza las validaciones de `User`
email: String # Reutiliza las validaciones de `User`
mobilePhone: String # Reutiliza las validaciones de `User`
status: UserStatus
}

Las directivas como @stringConstraint y @emailConstraint centralizan las reglas de validación, garantizando que los campos como fullName, email, y mobilePhone sigan las mismas restricciones, sin importar si se utilizan en un tipo o input.

2. Generador de Código para TypeScript y Go

Para aprovechar estas definiciones centralizadas, desarrollamos un generador de código que convierte las reglas de validación definidas en el esquema .gql en validaciones concretas para TypeScript y Go. En TypeScript, utilizamos la librería Yup para manejar las validaciones, mientras que en Go, usamos GoValidator.

Ejemplo de Generación Automática en TypeScript (Yup):
import * as yup from "yup";

class UserValidations {
fullNameValidation() {
return yup.string()
.required("Full Name is required")
.min(3, "Full Name must be at least 3 characters")
.max(50, "Full Name must be at most 50 characters")
.matches(/^[a-zA-Z ]+$/, "Full Name can only contain letters and spaces");
}

emailValidation() {
return yup.string()
.email("Please enter a valid email")
.required("Email is required");
}

mobilePhoneValidation() {
return yup.string()
.required("Mobile Phone is required")
.matches(/^[0-9]{7,15}$/, "Mobile Phone must be between 7 and 15 digits");
}

// Otras validaciones se generan de forma similar
}
Ejemplo de Generación Automática en Go (GoValidator):
import "github.com/asaskevich/govalidator"

func ValidateFullName(fullName string) error {
if len(fullName) < 3 || len(fullName) > 50 {
return errors.New("Full Name must be between 3 and 50 characters")
}
if !govalidator.Matches(fullName, "^[a-zA-Z ]+$") {
return errors.New("Full Name can only contain letters and spaces")
}
return nil
}

func ValidateEmail(email string) error {
if !govalidator.IsEmail(email) {
return errors.New("Please enter a valid email")
}
return nil
}

func ValidateMobilePhone(mobilePhone string) error {
if !govalidator.Matches(mobilePhone, "^[0-9]{7,15}$") {
return errors.New("Mobile Phone must be between 7 and 15 digits")
}
return nil
}

3. Ventajas de Centralizar Validaciones en el Esquema GraphQL

Al usar GraphQL como nuestro SSOT para validaciones, logramos:

  • Consistencia: Las mismas reglas de validación se aplican en todas las capas de la aplicación, asegurando que los datos se validen de manera uniforme.
  • Mantenibilidad: Si se cambia una regla de validación en el esquema GraphQL, el generador de código actualiza automáticamente las validaciones en TypeScript y Go, eliminando la necesidad de hacer cambios manuales en múltiples lugares.
  • Eficiencia: Al centralizar las validaciones, evitamos la duplicación de reglas y código, reduciendo el riesgo de errores.

Conclusión

Definir las validaciones en un esquema GraphQL como SSOT permite mantener la coherencia entre las reglas de validación en todo el sistema. Con nuestro generador de código, automatizamos la creación de validaciones para TypeScript (con Yup) y Go (con GoValidator), asegurando que los datos se validen de manera consistente y eficiente en todas las capas de la aplicación. Este enfoque centralizado mejora la mantenibilidad, reduce la duplicación de código y asegura que cualquier cambio en las reglas de validación se aplique de manera uniforme en toda la aplicación.

Trabajo Futuro

En el futuro, se podría expandir este enfoque para incluir validaciones dinámicas y más complejas, así como la posibilidad de extender las reglas a otros lenguajes o frameworks utilizados en el sistema.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *