Cordura en el Modelado de Contenidos: Cómo Estructurar Datos Complejos sin Perder la Cabeza

Programación· 6 min de lectura

Cordura en el Modelado de Contenidos: Cómo Estructurar Datos Complejos sin Perder la Cabeza

Hace poco estaba construyendo una aplicación de gestión de contenidos para un cliente español. Empecé con lo que parecía una idea simple: un sistema de páginas con bloques editables. A las dos semanas, mi base de datos parecía un nudo de cables en el sótano de una oficina de los 90s.

El problema no era la tecnología. Era cómo había modelado los datos.

El Dilema: Referencias vs Objetos Embebidos

Este es el primer punto de quiebre en cualquier proyecto medianamente complejo. Cuando tienes contenido relacionado, tienes dos caminos:

Opción 1: Referencias (Relaciones)

```json { "id": "page_123", "title": "Mi Página", "author_id": "user_456", "blocks": ["block_1", "block_2", "block_3"] }

{ "id": "user_456", "name": "Juan", "email": "juan@ejemplo.es" }

{ "id": "block_1", "type": "text", "content": "Contenido aquí" } ```

Opción 2: Objetos Embebidos

```json { "id": "page_123", "title": "Mi Página", "author": { "id": "user_456", "name": "Juan", "email": "juan@ejemplo.es" }, "blocks": [ { "id": "block_1", "type": "text", "content": "Contenido aquí" } ] } ```

Ambas funcionan. Pero tienen consecuencias muy diferentes.

Con referencias, obtienes flexibilidad. El usuario puede cambiar su email y se refleja en todos lados automáticamente. Pero necesitas hacer queries más complejas. En Supabase, terminas con joins. En bases de datos NoSQL, terminas haciendo múltiples llamadas.

Con objetos embebidos, obtienes velocidad. Una sola lectura te da todo lo que necesitas. Pero si el usuario cambia su email, necesitas actualizar cada página donde aparezca. Y eso escala mal rápidamente.

Mi regla práctica:

  • **Usa referencias** si el dato cambia frecuentemente y debe reflejarse en múltiples lugares
  • **Usa objetos embebidos** si el dato es relativamente estático o es específico del contexto

En el proyecto del cliente, los autores cambiaban raramente. Los bloques de contenido eran específicos de cada página. Así que embebí los bloques y usé referencias solo para el autor. Problema resuelto.

El Patrón Singleton: Cuándo es Cordura, Cuándo es Locura

Todo proyecto tiene esos datos que son "únicos". La configuración global. Las opciones del sitio. El tema de colores.

Muchos desarrolladores crean un documento singleton: un único registro que contiene todo.

```json { "id": "config_global", "site_name": "Mi Sitio", "primary_color": "#0066ff", "secondary_color": "#ff6600", "logo_url": "https://...", "footer_text": "© 2025", "analytics_id": "UA-123456", "stripe_key": "pk_live_...", "email_from": "noreply@ejemplo.es", "max_uploads_mb": 50, "feature_flags": { "new_editor": true, "beta_analytics": false } } ```

Esto parece lógico. Pero es un desastre esperando suceder.

Primero, todos tus servicios necesitan leer este documento constantemente. Si lo cacheas, tienes que invalidar el caché cada vez que algo cambia. Si no lo cacheas, cada request hace una lectura a la base de datos.

Segundo, los permisos se vuelven un problema. ¿Quién puede cambiar la configuración? ¿Todo el equipo? ¿Solo administradores? Un singleton global hace esto difícil de controlar.

Tercero, versioning. Si necesitas rollback de una configuración, no tienes historial.

Mi enfoque ahora:

Divido la configuración en contextos:

```json // settings/branding { "id": "branding", "site_name": "Mi Sitio", "primary_color": "#0066ff", "logo_url": "https://..." }

// settings/features { "id": "features", "new_editor": true, "beta_analytics": false }

// settings/integrations (con acceso restringido) { "id": "integrations", "stripe_key": "pk_live_...", "analytics_id": "UA-123456" } ```

Cada sección tiene su propio control de acceso. Puedo cachear branding agresivamente porque cambia raramente. Las features puedo cachear con menos tiempo porque cambian más seguido. Las integraciones nunca las cacheo.

Page Builders: El Arma de Doble Filo

Ahora llegamos a lo interesante. Los page builders son tentadores. Arrastra, suelta, publica. Los clientes los aman.

Pero el modelado de datos es donde todo se tuerce.

Un page builder típico genera algo como esto:

```json { "id": "page_landing", "title": "Landing Page", "slug": "landing", "sections": [ { "id": "section_1", "type": "hero", "props": { "title": "Bienvenido", "subtitle": "Tu descripción aquí", "background_image": "https://...", "cta_text": "Empezar ahora", "cta_url": "/signup" } }, { "id": "section_2", "type": "features", "props": { "title": "Características", "features": [ { "icon": "star", "title": "Rápido", "description": "Muy rápido" } ] } } ] } ```

Esto funciona. Pero tiene problemas:

1. Rigidez: Los componentes están hardcodeados. Si quieres agregar un nuevo tipo de sección, necesitas cambiar el código.

2. Reutilización: Si el mismo componente aparece en 10 páginas y quieres cambiarlo, tienes que actualizar 10 documentos.

3. Validación: ¿Cómo validas que los props de un hero sean válidos? ¿Qué pasa si el cliente agrega un campo que no debería estar ahí?

Mi enfoque:

Uso un patrón de "componentes como referencias":

```json // components/hero_1 { "id": "hero_1", "type": "hero", "title": "Bienvenido", "subtitle": "Tu descripción aquí", "background_image": "https://...", "cta_text": "Empezar ahora", "cta_url": "/signup" }

// pages/landing { "id": "page_landing", "title": "Landing Page", "slug": "landing", "sections": ["hero_1", "features_main", "testimonials_2"] } ```

Ahora:

  • Puedo reutilizar componentes en múltiples páginas
  • Si cambio el hero, se refleja en todos lados
  • Puedo validar cada tipo de componente independientemente
  • Es fácil versionar cambios

El trade-off es que el cliente no puede editar directamente en la página. Pero eso es un precio pequeño por cordura.

El Principio Subyacente

Todo esto viene de una idea simple: tus datos deben reflejar tu realidad de negocio, no la conveniencia técnica del momento.

Cuando diseñas mal, pagas después. Pagas en queries lentas. Pagas en bugs de sincronización. Pagas en features que son imposibles de agregar.

Cuando diseñas bien, todo se vuelve fácil. Agregar features es rápido. Cambios son simples. El código es legible.

Cómo Lo Hago Ahora

Antes de escribir una sola línea de código, dibujo:

1. Qué datos cambian frecuentemente → Referencias 2. Qué datos son estáticos o contextuales → Embebidos 3. Qué datos necesito cachear → Singletons divididos 4. Qué datos el usuario edita directamente → Componentes reutilizables

Esto me ahorra semanas después.

Takeaway

La cordura en programación es predecible. Viene de decisiones pequeñas tomadas temprano. El modelado de contenidos es donde más importa.

No es sexy. No es una nueva librería. Pero es la diferencia entre un proyecto que escala y uno que te mantiene despierto a las 3 de la mañana debuggeando sincronización de datos.

La próxima vez que empieces un proyecto, invierte una hora en dibujar tu estructura de datos. Pregúntate: ¿Qué cambia? ¿Qué es reutilizable? ¿Qué necesito cachear?

Te lo agradecerás en tres meses.

Brian Mena

Brian Mena

Ingeniero informatico construyendo productos digitales rentables: SaaS, directorios y agentes de IA. Todo desde cero, todo en produccion.

LinkedIn