Atos Portal
- Drupal 10
- PHP 8.2
- JSON:API
- Next.js
- React
- Tailwind CSS
- Redis
- MySQL
- Docker
- Nginx
Atos es un grupo de servicios tecnológicos presente en más de 70 países. Su web pública se había vuelto imposible de actualizar sin un desarrollador, así que la agencia a la que me incorporé la reconstruyó desde cero. Entregué tres de sus siete sistemas de contenido, y de paso corregí tres bugs en el propio framework de la agencia.
- 20Regiones atendidas
- 9Idiomas
- 300+Webmasters simultáneos
- 91 / 94 / 92Lighthouse rend. / a11y / prácticas
- 179 msTTFB — el objetivo eran 800 ms
El encargo
Atos es un grupo internacional de servicios tecnológicos presente en más de 70 países, con cerca de 100.000 empleados — socio informático mundial de los Juegos Olímpicos y Paralímpicos. Su portal corporativo es la puerta de entrada pública para clientes, inversores, socios y candidatos. Atos encargó la reconstrucción completa a VOID Agency, y yo entregué una parte como mi proyecto de fin de carrera de seis meses, dentro de un equipo ágil de ocho personas.
Por qué el antiguo portal tenía que desaparecer
La plataforma heredada era un monolito de WordPress con cuatro fallos estructurales:
- Parálisis editorial. Cada cambio de maquetación, cada nueva sección, cada variación regional necesitaba un desarrollador. Equipos editoriales repartidos por varios continentes quedaban bloqueados en tareas tan pequeñas como publicar una nota de prensa.
- Un modelo de contenido plano. Notas de prensa, premios, anuncios de eventos e informes de investigación vivían todos en una estructura indiferenciada — sin campos distintos, sin semántica, por lo que un filtrado preciso y una segmentación regional eran imposibles.
- SEO internacional roto. Las etiquetas
hreflangestaban sistemáticamente mal formadas: códigos de región incorrectos,x-defaultausente, URLs no canónicas. A visitantes de habla inglesa se les servían páginas en árabe. En 70 mercados, el daño se acumulaba. - Presupuestos de rendimiento inalcanzables. El renderizado de PHP bajo demanda en cada página hacía inalcanzables los umbrales de Core Web Vitals.
La arquitectura desacoplada
La respuesta fue una arquitectura headless. Drupal 10 pasó a ser un repositorio de contenido puro, aislado tras una red privada y accesible solo mediante endpoints JSON:API filtrados — lo que elimina por completo la superficie de ataque pública. Next.js renderiza el sitio público, pregenerando páginas de forma estática (SSG) y refrescándolas en segundo plano en cada cambio de contenido (ISR), con Redis absorbiendo la carga de lectura. Todo el stack corre en contenedores mediante Docker Compose en local, Easypanel en staging y AWS en producción.
Todo ello se apoya en Vactory, el framework de empresa propio de VOID, construido sobre el principio de Software Product Line, de modo que los módulos pueden compartirse entre los tres portales del grupo — Atos Groupe (ya en producción), Atos (este proyecto) y Eviden (el siguiente).
Lo que estuvo bajo mi responsabilidad
El portal reúne siete tipos de contenido editorial repartidos en dos familias. Tres fueron míos de principio a fin — Case Studies, Research Reports y Events & Webinars — desde el modelado de entidades en Drupal y los normalizers de JSON:API hasta los componentes React. Después auditué y corregí los cuatro módulos construidos por el resto del equipo antes de la entrega final.
Un segundo sprint entregó el sistema Featured: siete widgets que permiten a los webmasters elegir a mano el contenido prioritario en cualquier página de listado, mostrado como una tarjeta destacada para la familia Insights o como un carrusel animado para la familia Newsroom.
Motor de Insights
Tres de los siete módulos de contenido del portal, a cargo de principio a fin: Case Studies (enlazados de forma cruzada con informes y white papers), Research Reports (descargas de PDF restringidas a través de Adobe Marketo) y Events & Webinars (ponentes dinámicos construidos sobre la API Paragraphs de Drupal, más una llamada a la acción de registro que solo se muestra mientras el evento sigue siendo futuro).
Sistema Featured
Siete widgets repartidos en dos familias editoriales, todos delegando en una misma capa de renderizado compartida — una tarjeta destacada para Insights, un carrusel de avance automático de 7 segundos con indicadores de progreso, pausa al pasar el ratón y deslizamiento táctil para Newsroom. Un octavo widget solo necesitaría un archivo de configuración.
Región e idioma
Filtrado regional automático en 20 regiones y traducción completa a 9 idiomas, aplicados a listados, páginas de detalle y bloques de contenido cruzado, para que ningún artículo se filtre nunca fuera de su mercado objetivo.
Auditoría de módulos del equipo
Auditoría de los cuatro módulos construidos por mis compañeros, corrigiendo dos clases de defectos: parámetros de filtro regional ausentes en las consultas JSON:API (contenido apareciendo en mercados equivocados) y carencias de WCAG 2.1 AA en los componentes interactivos — roles ARIA, landmarks semánticos, etiquetas de diapositivas traducidas, pausa al pasar el ratón/táctil en la animación.
Contribuciones al framework Core
Durante la construcción surgieron tres limitaciones propias de Vactory. En cada caso, la decisión fue corregir el framework en lugar de rodearlo localmente — así que las tres se integraron en el perfil de instalación de la agencia y ahora benefician a todos los proyectos de VOID, pasados y futuros.
- 01
Reconstrucción de NodeQueueElement
- El problema
- El selector de artículos del framework era un simple campo de texto donde se escribían títulos de artículos separados por comas. Eso funcionaba en proyectos anteriores con títulos cortos. Los títulos de Atos tienen entre 150 y 200 caracteres, y el carrusel de Newsroom necesita hasta tres de ellos en un orden controlado — el campo era inutilizable, y bloqueaba la entrega de todo el módulo Featured Newsroom.
- Lo que hice
- Una reescritura completa del componente: un campo de búsqueda con autocompletado por posición, alta y baja gestionadas por AJAX sin recargar la página, un contador en vivo, y un máximo leído de forma declarativa desde el propio archivo de configuración del widget en lugar de estar codificado. El autocompletado respeta la región activa, de modo que un editor no puede elegir por error contenido de otro mercado.
- Por qué importaba
- El orden del carrusel queda ahora garantizado de principio a fin — el orden de arrastrar y soltar persiste gracias a una ordenación estable al guardar, y un paso de reordenación en el lado de Next.js lo restaura tras la petición, porque JSON:API no garantiza ningún orden de retorno. Cada futuro widget de la agencia con selector de artículos hereda todo esto gratis.
- 02
Un bug de unión de arrays en PHP
- El problema
- Los ajustes de los widgets no se guardaban. Un editor elegía una opción en un desplegable, checkbox o radio, la guardaba, reabría el formulario — y recuperaba el valor por defecto del archivo de configuración. Cualquier personalización duradera de un widget era imposible.
- Lo que hice
- La causa era
return $element_defaults + $element;. El operador de unión de arrays de PHP conserva el valor de la izquierda ante una colisión de claves, así que el#default_valuecodificado en el YAML siempre ganaba al cargado desde la base de datos. La corrección elimina esa clave del array de la izquierda — pero solo cuando existe realmente un valor guardado, para que los widgets genuinamente nuevos sigan recibiendo sus valores por defecto. - Por qué importaba
- Una regresión silenciosa de persistencia que afectaba a todos los campos no textuales del constructor de páginas, resuelta en pocas líneas y compartida con todos los equipos en esa versión del framework.
- 03
Localización de fechas en el servidor
- El problema
- Las fechas se formateaban y traducían en el frontend de Next.js. En proyectos anteriores de tres idiomas era sobrevivible, y bugs como una fecha en francés mostrada como 05/17/2026 en lugar de 17 mai 2026 pasaban inadvertidos. Con nueve idiomas era un problema de mantenimiento que solo crece.
- Lo que hice
- Construí AtosaiDateEnhancer, un plugin de JSON:API Field Enhancer que se engancha al pipeline de serialización de Drupal. Intercepta la marca de tiempo bruta y la sustituye por una cadena ya formateada y traducida por los propios servicios de fecha y traducción de Drupal, tomados del idioma de la petición. Un formulario de administración lista cada formato de fecha declarado en el back-office, así que asociar un formato a un campo es un solo clic, sin nada codificado a mano.
- Por qué importaba
- La lógica de fechas salió por completo del frontend, llevándose consigo las librerías de manejo de fechas del bundle de JavaScript, y las fechas multilingües pasaron a ser correctas por construcción en los nueve idiomas.
Lo que aprendí
Era mi primera vez construyendo a una escala genuinamente de empresa, y la lección fue que la arquitectura trata sobre todo de quién tendrá que vivir con ella. Veinte regiones en nueve idiomas significan que cada decisión de modelo de contenido se multiplica antes de que un editor la vea siquiera — y 300 personas editando a la vez es una restricción que no se puede añadir después. Trabajar a través de un Product Owner en lugar de directamente con el cliente me enseñó a defender decisiones técnicas en términos de negocio.
El aprendizaje más duradero vino de las tres contribuciones al framework. Corregir Vactory en lugar de rodearlo dentro de nuestro propio módulo significó leer código que yo no había escrito, encontrar el punto exacto de fallo, y escribir algo lo bastante general para proyectos que nunca veré. Es una disciplina distinta de hacer que funcione un solo sitio, y es la parte de este proyecto que repetiría primero.