Visión Arquitectónica

Principio director

La arquitectura se basa en el principio de separación de preocupaciones :

  • Contenido : archivos AsciiDoc estructurados con metadatos ricos

  • Presentación : plantillas Thymeleaf reutilizables

  • Datos : modelo extraído automáticamente por JBake desde los atributos AsciiDoc

  • Estilo: Bootstrap 5 para la coherencia visual

Elección estratégica: AsciiDoc para el portafolio

Table 1. Pourquoi AsciiDoc ?
Criterio Ventaja

Coherencia

Mismo formato que los artículos de blog

Metadatos

Atributos estructurados y extensibles (project-*)

mantenibilidad

Edición simple en texto, versionable Git

Flexibilidad

Puede contener contenido rico (tablas, código, imágenes)

Plantillado

JBake extrae automáticamente los atributos para Thymeleaf

Modelo de datos

Cada proyecto portfolio es un documento AsciiDoc:

  • Metadatos de encabezado : información estructurada (cliente, duración, tecnologías, etc.)

  • Cuerpo del documento : descripción narrativa, desafíos, soluciones, resultados

  • Atributos personalizados : prefijados`project-*`para la extracción automática

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false

class ProjetAsciiDoc {
  +titre: String
  +jbake-type: "proyecto"
  +jbake-status: published|draft
  +jbake-date: Date
  +jbake-tags: List<String>
  --
  +project-category: String
  +project-client: String
  +project-duration: String
  +project-role: String
  +project-thumbnail: String
  +project-gallery: List<String>
  +project-tech-stack: String
  +project-highlights: List<String>
  +project-demo-url: String
  +project-github-url: String
  --
  +body: HTML (converti)
}

class JBakeEngine {
  +parse(asciidoc)
  +extractMetadata()
  +convertToHTML()
}

class ThymeleafTemplate {
  +portfolio.html
  +project.html
  +partials/project-card.html
}

class PortfolioPage {
  +published_projects: List
  +filtres: categories
}

ProjetAsciiDoc --> JBakeEngine : analizar
JBakeEngine --> ThymeleafTemplate : proporciona datos
ThymeleafTemplate --> PortfolioPage : genera
@enduml

Casos de uso detallados

UC1 : Añadir un elemento al portafolio

Flujo nominal

@startuml
skinparam backgroundColor #FEFEFE
actor Développeur as dev
participant "Editor\nTexto" as editor
participant "Archivo\nAsciiDoc" as file
participant "JBake\nMotor" as jbake
participant "Sitio\nEstático" as site

dev -> editor : Crear nuevo archivo
activate editor
editor -> file : portfolio/nouveau-projet.adoc
activate file

dev -> editor : Redactar encabezado con metadatos\n(título, type=project, status=published,\natributos project-*)
dev -> editor : Redactar contenido narrativo\n(contexto, desafíos, soluciones)
dev -> editor : Agregar imágenes en assets/img/portfolio/

editor -> file : Guardar
deactivate editor

dev -> jbake : Ejecutar build (jbake -b)
activate jbake
jbake -> file : Leer y analizar
jbake -> jbake : Extraer metadatos
jbake -> jbake : Convertir AsciiDoc → HTML
jbake -> jbake : Aplicar plantilla project.html
jbake -> jbake : Añadir a la lista portfolio.html
jbake -> site : Generar páginas estáticas
deactivate jbake

dev -> site : Verificar resultado
activate site
site --> dev : Mostrar proyecto
deactivate site
@enduml

Plantilla del archivo a crear

El desarrollador crea`content/portfolio/nom-projet.adoc`con una estructura estandarizada:

  • Encabezado con todos los atributos requeridos

  • Secciones normalizadas (Contexto, Desafíos, Soluciones, Resultados)

  • Nomenclatura coherente de las imágenes

Puntos de validación

  • Los atributos obligatorios están presentes(jbake-type, jbake-status, project-thumbnail)

  • Las imágenes referenciadas existen en`assets/img/portfolio/`

  • La compilación de JBake se ejecuta sin errores

  • El proyecto aparece en la página portfolio

  • La página individual del proyecto se muestra correctamente

UC2 : Eliminar un elemento del portafolio

Flujo nominal

@startuml
skinparam backgroundColor #FEFEFE
actor Développeur as dev
participant "Sistema\nArchivos" as fs
participant "JBake\nMotor" as jbake
participant "Sitio\nEstático" as site
database "Caché\nJBake" as cache

dev -> fs : Eliminar portfolio/projet-ancien.adoc
activate fs
fs --> dev : Archivo eliminado
deactivate fs

dev -> fs : (Opcional) Eliminar imágenes asociadas\nassets/img/portfolio/projet-ancien-*
activate fs
fs --> dev : Imágenes eliminadas
deactivate fs

dev -> cache : Limpiar caché JBake
activate cache
cache --> dev : Caché vaciado
deactivate cache

dev -> jbake : Reconstrucción completa (jbake -b)
activate jbake
jbake -> jbake : Escanear content/portfolio/
jbake -> jbake : Proyecto ausente → no generado
jbake -> jbake : Regenerar portfolio.html\n(sin el proyecto eliminado)
jbake -> site : Desplegar nuevo build
deactivate jbake

dev -> site : Verificar
activate site
site --> dev : Proyecto ausente de la lista
deactivate site
@enduml

Estrategia alternativa: archivado

En lugar de eliminar definitivamente, posibilidad de crear una carpeta`content/portfolio/archive/`:

  • Mover el archivo en lugar de eliminarlo

  • Permite restaurar fácilmente

  • Mantén el historial de Git más claro

Limpieza de recursos

  • Comprobar las imágenes huérfanas en`assets/img/portfolio/`

  • Eliminar las imágenes no referenciadas por otros proyectos

  • Limpiar el caché de JBake para evitar referencias fantasma

UC3: Establecer como no publicado (borrador)

flujo nominal

@startuml
skinparam backgroundColor #FEFEFE
actor Développeur as dev
participant "Archivo\nAsciiDoc" as file
participant "JBake\nMotor" as jbake
participant "Sitio\nEstático" as site

dev -> file : Abrir portfolio/projet.adoc
activate file

dev -> file : Modificar atributo\n:jbake-status: published\n↓\n:jbake-status: draft

file --> dev : Guardado
deactivate file

dev -> jbake : Reconstruir (jbake -b)
activate jbake
jbake -> file : Parsear el archivo
jbake -> jbake : Detectar status=draft
jbake -> jbake : Excluir de published_projects
jbake -> jbake : No crear página pública
note right
El proyecto sigue existiendo
pero no está publicado
end note
jbake -> site : Generar sitio (sin este proyecto)
deactivate jbake

dev -> site : Verificar portafolio
activate site
site --> dev : Proyecto ausente de la lista pública
deactivate site
@enduml

Estados posibles

@startuml
skinparam backgroundColor #FEFEFE

[*] --> Draft : Création initiale
Draft --> Published : Validación y publicación
Published --> Draft : Retiro temporal
Draft --> Archived : Proyecto abandonado
Published --> Archived : Proyecto obsoleto
Archived --> [*] : Eliminación definitiva
Published --> Published : Actualizaciones

note right of Draft
:jbake-status: borrador
No visible públicamente
Útil para proyectos en curso
end note

note right of Published
:jbake-status: publicado
Visible en el portafolio
Indexado por JBake
end note

note right of Archived
Movido a archive/

o jbake-status: archivado

No publicado pero conservado
end note
@enduml

Casos de uso típicos

  • Proyecto en curso de redacción : crear en borrador, publicar cuando esté listo

  • Proyecto confidencial temporalmente : pasar a borrador mientras se llega al acuerdo con el cliente

  • Actualización importante: pasar a borrador, modificar, republicar

  • A/B testing : duplicar en borrador, probar, publicar la mejor versión

Arquitectura de plantillas

Estrategia de plantillas reutilizables

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

package "templates Thymeleaf" {
  component [index.html] as index
  component [portfolio.html] as portfolio
  component [project.html] as project

  package "Partials" {
    component [header.html] as header
    component [footer.html] as footer
    component [project-card.html] as card
    component [tech-badge.html] as badge
    component [gallery.html] as gallery
  }
}

package "Datos JBake" {
  database "published_projects" as data
  database "content (proyecto)" as content
}

index --> header
index --> footer

portfolio --> header
portfolio --> footer
portfolio --> card
data --> portfolio : iteración

project --> header
project --> footer
project --> badge
project --> gallery
content --> project : proyecto individual

note right of card
Fragmento reutilizable
para mostrar una tarjeta
de proyecto con:
- thumbnail
- titre
- tags
- highlights
end note
@enduml

Pattern de extracción de datos

JBake transforma automáticamente los atributos de AsciiDoc en propiedades accesibles en Thymeleaf :

@startuml
skinparam backgroundColor #FEFEFE

rectangle "Documento AsciiDoc" {
  (":project-client: Acme Corp") as attr1
  (":project-duration: 6 meses") as attr2
  (":project-tags: java, react") as attr3
}

rectangle "Modelo JBake" {
  (content['project-client']) as prop1
  (content['project-duration']) as prop2
  (content.tags) as prop3
}

rectangle "Plantilla Thymeleaf" {
  (th:text="${project['project-client']}") as tmpl1
  (th:text="${project['project-duration']}") as tmpl2
  (th:each="tag : ${project.tags}") as tmpl3
}

attr1 --> prop1 : parsing
attr2 --> prop2 : parsing
attr3 --> prop3 : parsing

prop1 --> tmpl1 : binding
prop2 --> tmpl2 : binding
prop3 --> tmpl3 : binding
@enduml

Convenciones de nomenclatura

  • Atributos del proyecto : prefijo`project-*` (ex: project-client, project-tech-stack)

  • Archivos : kebab-case (ej:`ecommerce-platform.adoc`)

  • Imágenes : prefijo nombre-proyecto (ej:`ecommerce-platform-thumb.jpg`)

  • Plantillas : nombre funcional (ej:)project-card.html, tech-badge.html)

Flujo de publicación

Pipeline de desarrollo

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false

class ProjetAsciiDoc {
  +titre: String
  +jbake-type: "proyecto"
  +jbake-status: published|draft
  +jbake-date: Date
  +jbake-tags: List<String>
  __
  +project-category: String
  +project-client: String
  +project-duration: String
  +project-role: String
  +project-thumbnail: String
  +project-gallery: List<String>
  +project-tech-stack: String
  +project-highlights: List<String>
  +project-demo-url: String
  +project-github-url: String
  __
  +body: HTML (converti)
}

class JBakeEngine {
  +parse(asciidoc)
  +extractMetadata()
  +convertToHTML()
}

class ThymeleafTemplate {
  +portfolio.html
  +project.html
  +partials/project-card.html
}

class PortfolioPage {
  +published_projects: List
  +filtres: categories
}

ProjetAsciiDoc --> JBakeEngine : analizar
JBakeEngine --> ThymeleafTemplate : proporciona datos
ThymeleafTemplate --> PortfolioPage : genera
@enduml

entornos

Entorno Uso Estado aceptado

Local

Desarrollo y preview

borrador, publicado

puesta en escena

Validación de preproducción

publicado únicamente

Producción

Sitio público

publicado únicamente

Extensibilidad

Agregado de nuevos atributos

Para enriquecer el modelo de datos, simplemente agregar nuevos atributos prefijados`project-*` :

  • project-awards: Premios y reconocimientos

  • `project-testimonial`Citación del cliente

  • `project-team-size`Tamaño del equipo

  • `project-budget-range`Rango presupuestario

Estos atributos se vuelven automáticamente disponibles en las plantillas sin modificar el motor JBake.

Categorización avanzada

@startuml
skinparam backgroundColor #FEFEFE

object Projet {
  project-category = "web"
  project-subcategory = "comercio electrónico"
  project-industry = "venta al por menor"
  project-complexity = "alto"
}

object Taxonomie {
  categories : [web, mobile, data, devops]
  subcategories : Map<category, List>
  industries : [retail, finance, healthcare, ...]
  complexities : [low, medium, high]
}

Projet --> Taxonomie : clasificado según

note right of Taxonomie
Permite filtrado multi‑criterio
en la plantilla portfolio.html
a través de JavaScript o del lado del servidor
end note
@enduml

Buenas Prácticas

Organización de los archivos

  • Archivo = proyecto : evitar mezclar varios proyectos

  • Imágenes en carpeta dedicadaassets/img/portfolio/nom-projet/

  • Nomenclatura coherente : facilita la búsqueda y el mantenimiento

  • Versioning Git : seguimiento completo de las modificaciones

Gestión del contenido

  • Estado borrador por defecto: publicar solo cuando esté listo

  • revisión antes de la publicación: validación de calidad y confidencialidad

  • Metadatos completos : completar todos los campos pertinentes

  • Contenido narrativo rico: no limitarse a los metadatos

Rendimiento

  • Optimizar imágenes : compresión antes del commit

  • Paginación si es necesario : si >20 proyectos

  • Carga diferida : imágenes de las galerías cargadas bajo demanda

  • Caché del navegador: encabezados adecuados para activos

Conclusión

Esta arquitectura permite:

  • Facilidad de uso: agregar un proyecto = crear un archivo de texto

  • Flexibilidad : extensible mediante nuevos atributos

  • Mantenibilidad : separación contenido/presentación

  • Trazabilidad : control de versiones Git completo

  • Automatización : build y despliegue continuo posibles

La elección de AsciiDoc garantiza la coherencia con el resto del sitio mientras ofrece la riqueza de los metadatos necesarios para un portafolio profesional.

Artículos relacionados