diff --git a/openspec/specs/cache-first-architecture/spec.md b/openspec/specs/cache-first-architecture/spec.md new file mode 100644 index 00000000..3300f0ee --- /dev/null +++ b/openspec/specs/cache-first-architecture/spec.md @@ -0,0 +1,154 @@ +# Especificacion de Arquitectura Cache-First + +## Purpose + +Define la arquitectura cache-first para ROITheme que permite que plugins de cache (W3TC, Redis, etc.) funcionen correctamente mientras plugins externos pueden evaluar condiciones ANTES de servir paginas. + +Esta arquitectura: +- Permite que las paginas se sirvan desde cache siempre que sea posible +- Provee hooks para que plugins externos (rate limiters, access control, etc.) evaluen condiciones +- Desacopla el tema de plugins especificos de restriccion de acceso +- Es portable: cualquier sitio con roi-theme tiene esta capacidad + +## Requirements + +### Requirement: Hook de Pre-Evaluacion de Pagina + +The system MUST provide a hook that fires BEFORE WordPress serves a page, allowing external plugins to evaluate conditions and potentially redirect. + +#### Scenario: Plugin externo necesita evaluar acceso antes de servir pagina +- **GIVEN** un plugin de control de acceso (rate limiter, membership, etc.) +- **WHEN** un visitante solicita una pagina singular (post, page, CPT) +- **THEN** el tema DEBE disparar `do_action('roi_theme_before_page_serve', $post_id)` +- **AND** el hook DEBE ejecutarse en `template_redirect` con priority 0 +- **AND** si el plugin llama `wp_safe_redirect()` y `exit`, la pagina NO se sirve + +#### Scenario: Ningn plugin enganchado al hook +- **GIVEN** ningun plugin esta escuchando `roi_theme_before_page_serve` +- **WHEN** un visitante solicita una pagina +- **THEN** la pagina se sirve normalmente (con cache si disponible) +- **AND** no hay impacto en rendimiento + +#### Scenario: Hook solo dispara en paginas singulares +- **GIVEN** el hook `roi_theme_before_page_serve` +- **WHEN** la solicitud es para archivo, home, search, feed, o admin +- **THEN** el hook NO DEBE dispararse +- **AND** la pagina se sirve sin evaluacion adicional + +#### Scenario: Hook no dispara para usuarios logueados +- **GIVEN** un usuario autenticado (logged in) +- **WHEN** solicita cualquier pagina +- **THEN** el hook NO DEBE dispararse +- **AND** la pagina se sirve directamente sin evaluacion +- **BECAUSE** WordPress no cachea paginas para usuarios logueados (cookies de sesion) + +--- + +### Requirement: Contexto Rico para Plugins Externos + +The system MUST provide sufficient context for external plugins to make access decisions. + +#### Scenario: Plugin necesita informacion del post +- **GIVEN** un plugin enganchado a `roi_theme_before_page_serve` +- **WHEN** el hook se dispara +- **THEN** el plugin recibe `$post_id` como parametro +- **AND** `get_queried_object()` retorna el objeto WP_Post completo +- **AND** `is_singular()`, `is_single()`, `is_page()` funcionan correctamente + +#### Scenario: Plugin necesita informacion del visitante +- **GIVEN** un plugin enganchado a `roi_theme_before_page_serve` +- **WHEN** el hook se dispara +- **THEN** `is_user_logged_in()` esta disponible +- **AND** `$_SERVER['REMOTE_ADDR']` esta disponible +- **AND** headers HTTP estan disponibles via `$_SERVER` + +--- + +### Requirement: No Bloquear Cache + +The system MUST NOT define DONOTCACHEPAGE or similar constants that prevent caching. + +#### Scenario: Tema no interfiere con plugins de cache +- **GIVEN** el tema roi-theme instalado +- **WHEN** W3TC, WP Super Cache, o Redis Object Cache estan activos +- **THEN** el tema NO DEBE definir `DONOTCACHEPAGE` +- **AND** el tema NO DEBE definir `DONOTCACHEOBJECT` +- **AND** el tema NO DEBE enviar headers `Cache-Control: no-cache` + +#### Scenario: Plugin externo decide bloquear cache +- **GIVEN** un plugin enganchado a `roi_theme_before_page_serve` +- **WHEN** el plugin necesita bloquear cache para una pagina especifica +- **THEN** es responsabilidad del PLUGIN definir `DONOTCACHEPAGE` +- **AND** el tema NO participa en esa decision + +--- + +### Requirement: Ubicacion en Clean Architecture + +The hook registration MUST follow ROITheme's Clean Architecture patterns. + +#### Scenario: Hook registrado en Infrastructure +- **GIVEN** el sistema de hooks del tema +- **WHEN** el hook `roi_theme_before_page_serve` es registrado +- **THEN** el registro DEBE estar en `Shared/Infrastructure/Hooks/` +- **AND** DEBE seguir el patron de HooksRegistrar existente + +#### Scenario: Servicio como punto de extension +- **GIVEN** la arquitectura del tema +- **WHEN** se necesita extender funcionalidad de pre-evaluacion +- **THEN** DEBE existir una interfaz en `Shared/Domain/Contracts/` +- **AND** la implementacion DEBE estar en `Shared/Infrastructure/Services/` + +--- + +## Implementation Notes + +### Hook Priority + +``` +template_redirect priority order: +├─ Priority 0: roi_theme_before_page_serve (tema dispara hook) +│ └─ Plugins externos se enganchan aqui +├─ Priority 1+: Otros plugins +└─ Priority 10 (default): WordPress template loading +``` + +### Ejemplo de Plugin Enganchado + +```php +// En un plugin externo (ej: ip-rate-limiter) +add_action('roi_theme_before_page_serve', function(int $post_id) { + // Evaluar condicion (ej: limite de IP) + if ($this->is_limit_exceeded()) { + wp_safe_redirect('/suscripcion-vip/?reason=limit', 302); + exit; + } + // Si no hay problema, simplemente retornar + // La pagina se servira (con cache si disponible) +}, 10); +``` + +### Archivos a Crear + +``` +Shared/ +├── Domain/ +│ └── Contracts/ +│ └── PageServeHookInterface.php # Interface del hook +└── Infrastructure/ + └── Hooks/ + └── CacheFirstHooksRegistrar.php # Registro del hook +``` + +--- + +## Acceptance Criteria + +1. El hook `roi_theme_before_page_serve` se dispara en `template_redirect` priority 0 +2. Solo se dispara para `is_singular() === true` +3. NO se dispara para usuarios logueados (`is_user_logged_in() === true`) +4. Pasa `$post_id` como parametro al hook +5. No define DONOTCACHEPAGE ni headers anti-cache +6. Plugins externos pueden enganchar y hacer redirect/exit +7. Si ningun plugin engancha, no hay impacto en rendimiento +8. Sigue patrones de Clean Architecture del tema