# Especificacion de Arquitectura Limpia ## Purpose Define la implementacion de Clean Architecture para ROITheme, un tema WordPress que sigue principios de Domain-Driven Design con separacion fisica de contextos delimitados (Admin, Public, Shared). > **NOTA**: Para convenciones de nomenclatura, ver `_openspec/specs/nomenclatura.md` > **NOTA**: Para principios SOLID y estandares de codigo, ver `_openspec/specs/estandares-codigo.md` > **NOTA**: Para flujo de trabajo y fases obligatorias, ver `_openspec/WORKFLOW-ROI-THEME.md` --- ## Requirements ### Requirement: Separacion Fisica de Contextos The system MUST organize code into three physically delimited contexts: Admin/, Public/, and Shared/. #### Scenario: Codigo pertenece al contexto de administracion - **WHEN** el codigo maneja operaciones CRUD, configuracion o funcionalidad del panel admin - **THEN** el codigo DEBE colocarse en el directorio `Admin/` - **AND** el codigo NO DEBE importar del directorio `Public/` #### Scenario: Codigo pertenece al contexto publico/frontend - **WHEN** el codigo maneja renderizado, visualizacion o presentacion frontend - **THEN** el codigo DEBE colocarse en el directorio `Public/` - **AND** el codigo NO DEBE importar del directorio `Admin/` #### Scenario: Codigo es compartido entre contextos - **WHEN** el codigo es usado por AMBOS contextos Admin/ y Public/ - **THEN** el codigo DEBE colocarse en el directorio `Shared/` raiz - **AND** tanto Admin/ como Public/ PUEDEN importar de Shared/ --- ### Requirement: Organizacion Granular de Codigo Compartido The system MUST implement three levels of shared code to avoid mixing context-specific shared code. #### Scenario: Codigo compartido solo dentro del contexto Admin - **WHEN** el codigo es reutilizado por multiples modulos Admin pero NO por Public - **THEN** el codigo DEBE colocarse en el directorio `Admin/Shared/` - **AND** los modulos de Public/ NO DEBEN importar de `Admin/Shared/` #### Scenario: Codigo compartido solo dentro del contexto Public - **WHEN** el codigo es reutilizado por multiples modulos Public pero NO por Admin - **THEN** el codigo DEBE colocarse en el directorio `Public/Shared/` - **AND** los modulos de Admin/ NO DEBEN importar de `Public/Shared/` #### Scenario: Codigo compartido entre ambos contextos - **WHEN** el codigo es reutilizado por AMBOS modulos Admin/ y Public/ - **THEN** el codigo DEBE colocarse en el directorio `Shared/` raiz - **AND** esto incluye ValueObjects, Exceptions y Contracts base --- ### Requirement: Cada Contexto Sigue las Capas de Clean Architecture Each context (Admin/, Public/, Shared/) MUST implement Infrastructure layer, and MAY implement Domain and Application layers when business logic requires it. #### Scenario: Estructura de modulo dentro del contexto Admin - **GIVEN** un componente llamado "Navbar" en el contexto Admin - **WHEN** el modulo es creado - **THEN** la estructura DEBE incluir: Admin/Navbar/Infrastructure/ - **AND** PUEDE incluir Domain/ (si hay logica de negocio) - **AND** PUEDE incluir Application/ (si hay casos de uso) #### Scenario: Estructura de modulo dentro del contexto Public - **GIVEN** un componente llamado "Navbar" en el contexto Public - **WHEN** el modulo es creado - **THEN** la estructura DEBE incluir: Public/Navbar/Infrastructure/ - **AND** PUEDE incluir Domain/ (si hay logica de negocio) - **AND** PUEDE incluir Application/ (si hay casos de uso) --- ### Requirement: Cumplimiento de Direccion de Dependencias The system MUST enforce that dependencies flow ONLY from outer layers to inner layers. #### Scenario: Infrastructure depende de Application y Domain - **WHEN** el codigo esta en la capa Infrastructure - **THEN** PUEDE importar de la capa Application - **AND** PUEDE importar de la capa Domain #### Scenario: Application depende solo de Domain - **WHEN** el codigo esta en la capa Application - **THEN** PUEDE importar de la capa Domain - **AND** NO DEBE importar de la capa Infrastructure #### Scenario: Domain no tiene dependencias externas - **WHEN** el codigo esta en la capa Domain - **THEN** NO DEBE importar de la capa Application - **AND** NO DEBE importar de la capa Infrastructure - **AND** NO DEBE importar funciones o globales de WordPress --- ### Requirement: La Capa Domain Contiene Solo Logica de Negocio Pura The Domain layer MUST contain only pure business logic without framework dependencies. #### Scenario: Validacion de contenido de capa Domain - **WHEN** el codigo se coloca en la capa Domain - **THEN** PUEDE contener Entities, Value Objects, Domain Services, Interfaces, Exceptions - **AND** NO DEBE contener global $wpdb, $_POST, $_GET, $_SESSION, add_action, add_filter, HTML, CSS, JavaScript #### Scenario: Implementacion de entidad Domain - **GIVEN** una entidad Domain como NavbarConfiguration - **WHEN** la entidad es implementada - **THEN** DEBE contener reglas de negocio y validacion - **AND** NO DEBE contener logica de persistencia - **AND** NO DEBE referenciar APIs de WordPress --- ### Requirement: La Capa Application Orquesta Domain The Application layer MUST orchestrate domain entities without containing business logic. #### Scenario: Implementacion de Use Case - **WHEN** un Use Case es implementado - **THEN** DEBE coordinar entidades y servicios de domain - **AND** DEBE depender de interfaces, NO de implementaciones concretas - **AND** NO DEBE contener reglas de validacion de negocio #### Scenario: Uso de DTOs para transferencia de datos - **WHEN** los datos cruzan limites entre capas - **THEN** se DEBEN usar DTOs (Data Transfer Objects) - **AND** los DTOs DEBEN ser contenedores de datos simples sin logica de negocio --- ### Requirement: Infrastructure Implementa Interfaces The Infrastructure layer MUST implement interfaces defined in Domain/Application layers. #### Scenario: Implementacion de Repository - **GIVEN** una RepositoryInterface definida en Domain - **WHEN** el repository es implementado - **THEN** DEBE colocarse en Infrastructure/Persistence/ - **AND** DEBE implementar la interface de Domain - **AND** PUEDE usar global $wpdb o APIs de WordPress #### Scenario: Integracion con WordPress - **WHEN** se necesita codigo especifico de WordPress - **THEN** DEBE colocarse en la capa Infrastructure - **AND** NO DEBE filtrarse a las capas Domain o Application --- ### Requirement: Los Modulos Son Autocontenidos e Independientes Each module (Navbar, Footer, Toolbar, etc.) MUST be self-contained and independent from other modules. #### Scenario: Aislamiento de modulos - **WHEN** un modulo como Admin/Navbar/ es implementado - **THEN** NO DEBE importar de Admin/Footer/ - **AND** NO DEBE importar de Admin/Toolbar/ - **AND** SOLO PUEDE importar de Shared/ #### Scenario: Eliminacion de modulos - **WHEN** un modulo necesita ser eliminado - **THEN** borrar la carpeta del modulo NO DEBE romper otros modulos - **AND** no se DEBERIAN requerir cambios de codigo en otros modulos --- ### Requirement: Admin y Public Son Bounded Contexts Separados Admin/ and Public/ MUST be treated as separate bounded contexts because they have different responsibilities. #### Scenario: Responsabilidad del contexto Admin - **WHEN** el codigo maneja administracion de componentes - **THEN** la entidad Domain se enfoca en configuracion, validacion, estados draft/published - **AND** los Use Cases se enfocan en operaciones Save, Update, Delete, Get #### Scenario: Responsabilidad del contexto Public - **WHEN** el codigo maneja renderizado de componentes - **THEN** la entidad Domain se enfoca en estado activo, caching, filtrado por permisos - **AND** los Use Cases se enfocan en operaciones GetActive, Render, Cache #### Scenario: No hay duplicacion de domain - **WHEN** Admin/Navbar/Domain/ y Public/Navbar/Domain/ ambos existen - **THEN** NO son duplicados sino bounded contexts especializados - **AND** Admin se enfoca en configuracion/gestion - **AND** Public se enfoca en renderizado/visualizacion --- ### Requirement: Validacion de Arquitectura Antes de Commit The system MUST validate architectural compliance before committing code. #### Scenario: Validacion de capa Domain - **WHEN** se valida codigo de la capa Domain - **THEN** grep por global $wpdb DEBE retornar vacio - **AND** grep por add_action DEBE retornar vacio - **AND** grep por $_POST DEBE retornar vacio #### Scenario: Validacion de dependencias de modulos - **WHEN** se validan dependencias entre modulos - **THEN** imports de Admin/Navbar/ desde Admin/Footer/ NO DEBEN existir - **AND** imports de Public/Navbar/ desde Public/Footer/ NO DEBEN existir --- ### Requirement: Realizacion de Beneficios de la Arquitectura The architecture MUST provide measurable benefits. #### Scenario: Asignacion granular de trabajo - **WHEN** un desarrollador es asignado a trabajar en Admin/Navbar/ - **THEN** puede acceder SOLO a esa carpeta - **AND** no puede ver ni modificar Public/ u otros modulos de Admin/ #### Scenario: Eliminacion facil de modulos - **WHEN** un componente ya no es necesario - **THEN** eliminarlo requiere solo borrar la carpeta - **AND** no se necesitan otras modificaciones de codigo #### Scenario: Codigo compartido consistente - **WHEN** se encuentra un bug en un ValueObject compartido - **THEN** arreglarlo en Shared/Domain/ValueObjects/ lo arregla para TODOS los modulos - **AND** no se necesita actualizar codigo duplicado --- ## Diagrama ASCII de Capas ``` ╔═══════════════════════════════════════════════════════════════════════════════╗ ║ CLEAN ARCHITECTURE ║ ║ ROI Theme ║ ╠═══════════════════════════════════════════════════════════════════════════════╣ ║ ║ ║ ┌─────────────────────────────────────────────────────────────────────┐ ║ ║ │ │ ║ ║ │ INFRASTRUCTURE │ ║ ║ │ (Capa Externa - WordPress) │ ║ ║ │ │ ║ ║ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │ ║ ║ │ │ Ui/ │ │ Api/ │ │ Persistence/│ │ Services/ │ │ ║ ║ │ │ Renderers │ │ AJAX │ │ Repositories│ │ Helpers │ │ ║ ║ │ │ FormBuilders│ │ Handlers │ │ $wpdb │ │ │ │ ║ ║ │ └─────────────┘ └─────────────┘ └─────────────┘ └───────────┘ │ ║ ║ │ │ ║ ║ │ PERMITIDO: WordPress APIs, HTML, CSS, JS, $wpdb, hooks │ ║ ║ │ │ ║ ║ └─────────────────────────────────────────────────────────────────────┘ ║ ║ │ ║ ║ ▼ depende de ║ ║ ┌─────────────────────────────────────────────────────────────────────┐ ║ ║ │ │ ║ ║ │ APPLICATION │ ║ ║ │ (Casos de Uso / Orquestacion) │ ║ ║ │ │ ║ ║ │ ┌─────────────────────────────────────────────────────────────┐ │ ║ ║ │ │ UseCases/ │ │ ║ ║ │ │ GetComponentSettingsUseCase, RenderComponentUseCase │ │ ║ ║ │ │ CheckVisibilityUseCase, SyncSchemaUseCase │ │ ║ ║ │ └─────────────────────────────────────────────────────────────┘ │ ║ ║ │ │ ║ ║ │ PERMITIDO: Orquestacion, DTOs, llamadas a interfaces Domain │ ║ ║ │ PROHIBIDO: WordPress, HTML, $wpdb, persistencia directa │ ║ ║ │ │ ║ ║ └─────────────────────────────────────────────────────────────────────┘ ║ ║ │ ║ ║ ▼ depende de ║ ║ ┌─────────────────────────────────────────────────────────────────────┐ ║ ║ │ │ ║ ║ │ DOMAIN │ ║ ║ │ (Centro - Logica de Negocio Pura) │ ║ ║ │ │ ║ ║ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────────────┐ │ ║ ║ │ │ Entities/ │ │Contracts/ │ │ Value │ │ Exceptions/ │ │ ║ ║ │ │Component │ │Interfaces │ │ Objects/ │ │ValidationException│ │ ║ ║ │ └───────────┘ └───────────┘ └───────────┘ └───────────────────┘ │ ║ ║ │ │ ║ ║ │ PERMITIDO: Reglas de negocio puras, validaciones, interfaces │ ║ ║ │ PROHIBIDO: WordPress, HTML, CSS, JS, $wpdb, echo, print │ ║ ║ │ │ ║ ║ └─────────────────────────────────────────────────────────────────────┘ ║ ║ ║ ║ REGLA DE DEPENDENCIA: Las flechas SOLO apuntan hacia adentro ║ ║ Infrastructure → Application → Domain ║ ║ NUNCA al reves: Domain NO depende de nada externo ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════════════════╝ ``` --- ## Estructura Completa de Carpetas del Tema ``` roi-theme/ │ ├── functions.php # Bootstrap del tema ├── style.css # Metadata del tema │ ├── Schemas/ # JSON schemas de componentes │ ├── contact-form.json │ ├── featured-image.json │ ├── footer.json │ └── ... │ ├── Admin/ # CONTEXTO: Panel de administracion │ ├── ContactForm/ # Modulo: Configuracion de ContactForm │ │ ├── Domain/ # (opcional si no hay logica especifica) │ │ ├── Application/ # (opcional si no hay use cases) │ │ └── Infrastructure/ │ │ └── Ui/ │ │ └── ContactFormFormBuilder.php │ │ │ ├── FeaturedImage/ │ │ └── Infrastructure/ │ │ └── Ui/ │ │ └── FeaturedImageFormBuilder.php │ │ │ ├── Shared/ # Compartido SOLO dentro de Admin │ │ └── Infrastructure/ │ │ └── Ui/ │ │ ├── AdminDashboardRenderer.php │ │ └── ExclusionFormPartial.php │ │ │ └── ... # Otros modulos Admin │ ├── Public/ # CONTEXTO: Frontend publico │ ├── ContactForm/ # Modulo: Renderizado de ContactForm │ │ ├── Domain/ # (opcional) │ │ │ └── Contracts/ │ │ ├── Application/ # (opcional) │ │ │ └── UseCases/ │ │ └── Infrastructure/ │ │ ├── Ui/ │ │ │ └── ContactFormRenderer.php │ │ └── Api/ │ │ └── WordPress/ │ │ └── ContactFormAjaxHandler.php │ │ │ ├── FeaturedImage/ │ │ └── Infrastructure/ │ │ └── Ui/ │ │ └── FeaturedImageRenderer.php │ │ │ ├── Shared/ # Compartido SOLO dentro de Public │ │ └── Infrastructure/ │ │ └── Services/ │ │ │ └── ... # Otros modulos Public │ ├── Shared/ # CONTEXTO: Compartido entre Admin Y Public │ ├── Domain/ │ │ ├── Contracts/ # Interfaces compartidas │ │ │ ├── RendererInterface.php │ │ │ ├── CSSGeneratorInterface.php │ │ │ ├── ComponentRepositoryInterface.php │ │ │ └── ... │ │ ├── Entities/ │ │ │ └── Component.php │ │ └── ValueObjects/ │ │ │ ├── Application/ │ │ └── UseCases/ │ │ └── CheckWrapperVisibilityUseCase.php │ │ │ └── Infrastructure/ │ ├── Services/ │ │ ├── CSSGeneratorService.php │ │ └── PageVisibilityHelper.php │ ├── Persistence/ │ │ └── WordPress/ │ │ ├── ComponentSettingsRepository.php │ │ └── PageVisibilityRepository.php │ └── Scripts/ │ └── validate-architecture.php │ ├── _openspec/ # Sistema de especificaciones │ ├── AGENTS.md │ ├── WORKFLOW-ROI-THEME.md │ ├── project.md │ ├── specs/ # Specs BASE (archivos planos) │ │ ├── arquitectura-limpia.md │ │ ├── estandares-codigo.md │ │ └── nomenclatura.md │ └── changes/ # Specs de features (carpetas) │ └── [nombre-feature]/ │ └── _planificacion/ # Documentos de planificacion ├── 01-design-system/ # Design System del tema ├── roi-theme-template/ # Template HTML de referencia ├── analisis-spam-formularios.md └── plan-mejora-especificaciones-openspec.md ``` --- ## Reglas de Anidamiento ### Requirement: Profundidad Maxima de Carpetas La estructura de carpetas DEBE respetar una profundidad maxima. #### Scenario: Profundidad maxima de 4 niveles desde contexto - **WHEN** se crea una estructura de carpetas - **THEN** la profundidad maxima DEBE ser 4 niveles desde el contexto - **AND** ejemplo valido: `Public/ContactForm/Infrastructure/Ui/` (4 niveles) - **AND** ejemplo valido: `Public/ContactForm/Infrastructure/Api/WordPress/` (5 niveles - excepcion para WordPress) - **AND** ejemplo invalido: `Public/ContactForm/Infrastructure/Ui/Partials/Helpers/` (6 niveles) #### Scenario: Regla de 3 archivos para subcarpetas - **WHEN** se decide crear una subcarpeta - **THEN** DEBE haber al menos 3 archivos que la justifiquen - **AND** si hay menos de 3 archivos, mantenerlos en la carpeta padre - **AND** ejemplo: NO crear `Ui/Helpers/` con solo 1-2 archivos #### Scenario: Subcarpetas permitidas en Infrastructure - **WHEN** se organizan archivos dentro de Infrastructure/ - **THEN** subcarpetas permitidas son: - `Ui/` - Renderers, FormBuilders, presentacion - `Api/` - Handlers AJAX, REST endpoints - `Api/WordPress/` - Handlers especificos de WordPress - `Persistence/` - Repositorios genericos - `Persistence/WordPress/` - Repositorios WordPress - `Services/` - Servicios de infraestructura - **AND** NO crear subcarpetas adicionales sin justificacion --- ## Diferencia Entre Niveles de Shared ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ NIVELES DE CODIGO COMPARTIDO │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ NIVEL 1: Shared/ (raiz) │ │ ───────────────────── │ │ QUIEN PUEDE USAR: Admin/ y Public/ │ │ CONTENIDO: Contratos base, entidades core, servicios fundamentales │ │ EJEMPLOS: │ │ - Shared/Domain/Contracts/RendererInterface.php │ │ - Shared/Domain/Contracts/CSSGeneratorInterface.php │ │ - Shared/Domain/Entities/Component.php │ │ - Shared/Infrastructure/Services/CSSGeneratorService.php │ │ │ │ NIVEL 2: Admin/Shared/ │ │ ────────────────────── │ │ QUIEN PUEDE USAR: SOLO modulos dentro de Admin/ │ │ CONTENIDO: UI components admin, helpers de formularios, partials │ │ EJEMPLOS: │ │ - Admin/Shared/Infrastructure/Ui/AdminDashboardRenderer.php │ │ - Admin/Shared/Infrastructure/Ui/ExclusionFormPartial.php │ │ PROHIBIDO PARA: Public/ │ │ │ │ NIVEL 3: Public/Shared/ │ │ ─────────────────────── │ │ QUIEN PUEDE USAR: SOLO modulos dentro de Public/ │ │ CONTENIDO: Helpers de renderizado, componentes frontend compartidos │ │ EJEMPLOS: │ │ - Public/Shared/Infrastructure/Services/RenderHelper.php │ │ PROHIBIDO PARA: Admin/ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ REGLA: Siempre colocar codigo en el nivel MAS ESPECIFICO posible. Solo subir a Shared/ raiz si AMBOS contextos lo necesitan. ``` --- ## Ejemplos de Codigo PHP Por Capa ### Ejemplo CORRECTO: Capa Domain (Interface) ```php name; } public function getData(): array { return $this->data; } public function isEnabled(): bool { return ($this->data['visibility']['is_enabled'] ?? false) === true; } } ``` ### Ejemplo INCORRECTO: Capa Domain (Entity con WordPress) ```php insert('wp_components', $this->data); } // ❌ INCORRECTO: HTML en Domain public function renderHtml(): string { return '
' . esc_html($this->name) . '
'; // ❌ PROHIBIDO } } ``` ### Ejemplo CORRECTO: Capa Application (UseCase) ```php repository->findByName($componentName); } } ``` ### Ejemplo INCORRECTO: Capa Application (UseCase con WordPress) ```php get_row("SELECT * FROM ..."); // ❌ INCORRECTO: Funciones WordPress en Application return get_option('roi_component_' . $componentName); } } ``` ### Ejemplo CORRECTO: Capa Infrastructure (Renderer) ```php getData(); // ✅ WordPress permitido en Infrastructure $nonce = wp_create_nonce('roi_contact_form'); // ✅ HTML permitido en Infrastructure $html = '
'; // ... mas HTML $html .= '
'; return $html; } public function supports(string $componentType): bool { return $componentType === self::COMPONENT_NAME; } } ``` ### Ejemplo CORRECTO: Capa Infrastructure (Repository) ```php prefix . 'roi_theme_component_settings'; $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE component_name = %s", $componentName ), ARRAY_A ); if (empty($results)) { return null; } return new Component($componentName, $this->groupResults($results)); } } ``` --- ## Mapeo de Terminologia | Clean Architecture Estandar | ROI Theme | Ubicacion | |---------------------------|-----------|-----------| | Entity | Component, Entity | `*/Domain/Entities/` | | Value Object | Value Object | `*/Domain/ValueObjects/` | | Repository Interface | RepositoryInterface | `Shared/Domain/Contracts/` | | Repository Implementation | Repository | `*/Infrastructure/Persistence/WordPress/` | | Use Case / Interactor | UseCase | `*/Application/UseCases/` | | Gateway | Repository | `*/Infrastructure/Persistence/` | | Presenter | Renderer | `Public/*/Infrastructure/Ui/` | | Controller | FormBuilder, Handler | `Admin/*/Infrastructure/Ui/`, `*/Infrastructure/Api/` | | DTO | Request/Response | `*/Application/DTOs/` | | Domain Service | Service | `*/Domain/Services/` | | Infrastructure Service | Service | `*/Infrastructure/Services/` | --- ## Validacion de Arquitectura ### Script de Validacion Ubicacion: `Shared/Infrastructure/Scripts/validate-architecture.php` ```bash # Validar un componente especifico php Shared/Infrastructure/Scripts/validate-architecture.php contact-form # Validar todos los componentes php Shared/Infrastructure/Scripts/validate-architecture.php --all ``` ### Que Valida el Script | Validacion | Que Busca | Error si Encuentra | |------------|-----------|-------------------| | WordPress en Domain | `global $wpdb`, `add_action`, `$_POST` | "WordPress code in Domain layer" | | HTML en Domain | `