refactor: reorganizar openspec y planificacion con spec recaptcha
- renombrar openspec/ a _openspec/ (carpeta auxiliar) - mover specs de features a changes/ - crear specs base: arquitectura-limpia, estandares-codigo, nomenclatura - migrar _planificacion/ con design-system y roi-theme-template - agregar especificacion recaptcha anti-spam (proposal, tasks, spec) - corregir rutas y referencias en todas las specs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
777
_openspec/specs/arquitectura-limpia.md
Normal file
777
_openspec/specs/arquitectura-limpia.md
Normal file
@@ -0,0 +1,777 @@
|
||||
# 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
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Domain\Contracts;
|
||||
|
||||
use ROITheme\Shared\Domain\Entities\Component;
|
||||
|
||||
/**
|
||||
* Interface para renderizadores de componentes.
|
||||
*
|
||||
* NOTA: Esta interface esta en Domain porque define el CONTRATO
|
||||
* que deben cumplir los renderizadores, sin detalles de implementacion.
|
||||
*/
|
||||
interface RendererInterface
|
||||
{
|
||||
/**
|
||||
* Renderiza un componente y retorna HTML.
|
||||
*/
|
||||
public function render(Component $component): string;
|
||||
|
||||
/**
|
||||
* Verifica si este renderer soporta el tipo de componente.
|
||||
*/
|
||||
public function supports(string $componentType): bool;
|
||||
}
|
||||
```
|
||||
|
||||
### Ejemplo INCORRECTO: Capa Domain (Interface con WordPress)
|
||||
|
||||
```php
|
||||
<?php
|
||||
// ❌ INCORRECTO: WordPress en Domain
|
||||
namespace ROITheme\Shared\Domain\Contracts;
|
||||
|
||||
interface RendererInterface
|
||||
{
|
||||
// ❌ INCORRECTO: Dependencia de WordPress (WP_Post)
|
||||
public function render(WP_Post $post): string;
|
||||
|
||||
// ❌ INCORRECTO: HTML en Domain
|
||||
public function getDefaultHtml(): string;
|
||||
}
|
||||
```
|
||||
|
||||
### Ejemplo CORRECTO: Capa Domain (Entity)
|
||||
|
||||
```php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Domain\Entities;
|
||||
|
||||
/**
|
||||
* Entidad que representa un componente del tema.
|
||||
* Contiene SOLO logica de negocio pura.
|
||||
*/
|
||||
final class Component
|
||||
{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private array $data
|
||||
) {}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->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
|
||||
<?php
|
||||
// ❌ INCORRECTO: WordPress y persistencia en Domain
|
||||
namespace ROITheme\Shared\Domain\Entities;
|
||||
|
||||
final class Component
|
||||
{
|
||||
// ❌ INCORRECTO: Acceso a BD en Domain
|
||||
public function save(): void
|
||||
{
|
||||
global $wpdb; // ❌ PROHIBIDO
|
||||
$wpdb->insert('wp_components', $this->data);
|
||||
}
|
||||
|
||||
// ❌ INCORRECTO: HTML en Domain
|
||||
public function renderHtml(): string
|
||||
{
|
||||
return '<div>' . esc_html($this->name) . '</div>'; // ❌ PROHIBIDO
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Ejemplo CORRECTO: Capa Application (UseCase)
|
||||
|
||||
```php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Public\ContactForm\Application\UseCases;
|
||||
|
||||
use ROITheme\Shared\Domain\Contracts\ComponentRepositoryInterface;
|
||||
use ROITheme\Shared\Domain\Entities\Component;
|
||||
|
||||
/**
|
||||
* Caso de uso: Obtener configuracion de un componente.
|
||||
* Orquesta llamadas a repositorios, NO contiene logica de negocio.
|
||||
*/
|
||||
final class GetComponentSettingsUseCase
|
||||
{
|
||||
public function __construct(
|
||||
private ComponentRepositoryInterface $repository // ✅ Interface, no clase concreta
|
||||
) {}
|
||||
|
||||
public function execute(string $componentName): ?Component
|
||||
{
|
||||
// ✅ Solo orquestacion, delega a repository
|
||||
return $this->repository->findByName($componentName);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Ejemplo INCORRECTO: Capa Application (UseCase con WordPress)
|
||||
|
||||
```php
|
||||
<?php
|
||||
// ❌ INCORRECTO: WordPress directo en Application
|
||||
namespace ROITheme\Public\ContactForm\Application\UseCases;
|
||||
|
||||
final class GetComponentSettingsUseCase
|
||||
{
|
||||
public function execute(string $componentName): array
|
||||
{
|
||||
// ❌ INCORRECTO: $wpdb directo en Application
|
||||
global $wpdb;
|
||||
return $wpdb->get_row("SELECT * FROM ...");
|
||||
|
||||
// ❌ INCORRECTO: Funciones WordPress en Application
|
||||
return get_option('roi_component_' . $componentName);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Ejemplo CORRECTO: Capa Infrastructure (Renderer)
|
||||
|
||||
```php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Public\ContactForm\Infrastructure\Ui;
|
||||
|
||||
use ROITheme\Shared\Domain\Contracts\RendererInterface;
|
||||
use ROITheme\Shared\Domain\Contracts\CSSGeneratorInterface;
|
||||
use ROITheme\Shared\Domain\Entities\Component;
|
||||
|
||||
/**
|
||||
* Renderer para el formulario de contacto.
|
||||
* Implementa RendererInterface de Domain.
|
||||
*/
|
||||
final class ContactFormRenderer implements RendererInterface
|
||||
{
|
||||
private const COMPONENT_NAME = 'contact-form';
|
||||
|
||||
public function __construct(
|
||||
private CSSGeneratorInterface $cssGenerator // ✅ DI via interface
|
||||
) {}
|
||||
|
||||
public function render(Component $component): string
|
||||
{
|
||||
$data = $component->getData();
|
||||
|
||||
// ✅ WordPress permitido en Infrastructure
|
||||
$nonce = wp_create_nonce('roi_contact_form');
|
||||
|
||||
// ✅ HTML permitido en Infrastructure
|
||||
$html = '<form id="roiContactForm" data-nonce="' . esc_attr($nonce) . '">';
|
||||
// ... mas HTML
|
||||
$html .= '</form>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function supports(string $componentType): bool
|
||||
{
|
||||
return $componentType === self::COMPONENT_NAME;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Ejemplo CORRECTO: Capa Infrastructure (Repository)
|
||||
|
||||
```php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Infrastructure\Persistence\WordPress;
|
||||
|
||||
use ROITheme\Shared\Domain\Contracts\ComponentRepositoryInterface;
|
||||
use ROITheme\Shared\Domain\Entities\Component;
|
||||
|
||||
/**
|
||||
* Repository WordPress para componentes.
|
||||
* Implementa interface de Domain usando $wpdb.
|
||||
*/
|
||||
final class ComponentSettingsRepository implements ComponentRepositoryInterface
|
||||
{
|
||||
public function findByName(string $componentName): ?Component
|
||||
{
|
||||
global $wpdb; // ✅ WordPress permitido en Infrastructure
|
||||
|
||||
$table = $wpdb->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 | `<div`, `<form`, `echo` | "HTML/Output in Domain layer" |
|
||||
| Imports cruzados | `Admin/X` importando de `Admin/Y` | "Cross-module import detected" |
|
||||
| Direccion dependencias | Application importando Infrastructure | "Invalid dependency direction" |
|
||||
| Nomenclatura | Carpetas no-PascalCase | "Invalid folder naming" |
|
||||
| Interface implementation | Renderer sin RendererInterface | "Missing interface implementation" |
|
||||
| Strict types | Archivo sin `declare(strict_types=1)` | "Missing strict_types declaration" |
|
||||
|
||||
### Checklist Pre-Commit de Arquitectura
|
||||
|
||||
```
|
||||
[ ] Archivos Domain NO contienen: $wpdb, add_action, $_POST, echo, HTML
|
||||
[ ] Archivos Application NO contienen: $wpdb, HTML, WordPress functions
|
||||
[ ] Clases Infrastructure implementan interfaces de Domain
|
||||
[ ] No hay imports cruzados entre modulos del mismo contexto
|
||||
[ ] Carpetas siguen nomenclatura PascalCase
|
||||
[ ] Archivos PHP tienen declare(strict_types=1)
|
||||
[ ] DI es via constructor con interfaces
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Última actualización:** 2026-01-08
|
||||
1235
_openspec/specs/estandares-codigo.md
Normal file
1235
_openspec/specs/estandares-codigo.md
Normal file
File diff suppressed because it is too large
Load Diff
687
_openspec/specs/nomenclatura.md
Normal file
687
_openspec/specs/nomenclatura.md
Normal file
@@ -0,0 +1,687 @@
|
||||
# Especificacion de Nomenclatura - ROI Theme
|
||||
|
||||
## Purpose
|
||||
|
||||
Define las convenciones de nomenclatura para carpetas, archivos, clases, metodos, propiedades y variables en el tema ROI Theme (PHP 8.x / WordPress).
|
||||
|
||||
> **NOTA**: Para principios SOLID y estandares de codigo, ver `_openspec/specs/estandares-codigo.md`
|
||||
> **NOTA**: Para arquitectura y modularidad, ver `_openspec/specs/arquitectura-limpia.md`
|
||||
|
||||
---
|
||||
|
||||
## Resumen de Nomenclaturas
|
||||
|
||||
| Elemento | Nomenclatura | Ejemplo |
|
||||
|----------|-------------|---------|
|
||||
| **Carpetas principales** | PascalCase | `Admin/`, `Public/`, `Shared/` |
|
||||
| **Carpetas de contexto** | PascalCase | `Admin/`, `Public/` |
|
||||
| **Carpetas de modulo** | PascalCase | `ContactForm/`, `FeaturedImage/` |
|
||||
| **Carpetas de capa** | PascalCase | `Domain/`, `Application/`, `Infrastructure/` |
|
||||
| **Carpetas auxiliares** | _minusculas | `_planificacion/`, `_arquitectura/` |
|
||||
| **Archivos PHP de clase** | PascalCase.php | `ContactFormRenderer.php` |
|
||||
| **Archivos PHP de interface** | PascalCaseInterface.php | `RendererInterface.php` |
|
||||
| **Archivos JSON schema** | kebab-case.json | `contact-form.json` |
|
||||
| **Namespaces** | PascalCase | `ROITheme\Public\ContactForm\Infrastructure\Ui` |
|
||||
| **Clases** | PascalCase | `ContactFormRenderer` |
|
||||
| **Interfaces** | PascalCase + Interface | `RendererInterface`, `CSSGeneratorInterface` |
|
||||
| **Metodos** | camelCase | `render()`, `getVisibilityClass()` |
|
||||
| **Propiedades** | camelCase | `$cssGenerator`, `$componentName` |
|
||||
| **Variables locales** | $camelCase | `$showDesktop`, `$visibilityClass` |
|
||||
| **Parametros** | $camelCase | `$component`, `$data` |
|
||||
| **Campos privados** | $camelCase | `$cssGenerator` (con private) |
|
||||
| **Constantes clase** | UPPER_SNAKE_CASE | `COMPONENT_NAME`, `MAX_ITEMS` |
|
||||
| **Constantes globales** | UPPER_SNAKE_CASE | `ROI_THEME_VERSION` |
|
||||
| **component_name** | kebab-case | `"contact-form"`, `"featured-image"` |
|
||||
| **Hooks WordPress** | snake_case con prefijo | `roi_theme_after_render` |
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Nomenclatura de Carpetas
|
||||
|
||||
Los nombres de carpetas siguen convenciones basadas en su proposito.
|
||||
|
||||
#### Scenario: Carpetas principales del tema
|
||||
- **WHEN** se nombra una carpeta principal de codigo
|
||||
- **THEN** DEBE usar PascalCase
|
||||
- **AND** ejemplos correctos: `Admin/`, `Public/`, `Shared/`, `Schemas/`
|
||||
- **AND** ejemplos incorrectos: `admin/`, `ADMIN/`, `shared_code/`
|
||||
|
||||
#### Scenario: Carpetas de contexto
|
||||
- **WHEN** se nombra una carpeta que representa un contexto de la aplicacion
|
||||
- **THEN** DEBE usar PascalCase
|
||||
- **AND** carpetas permitidas:
|
||||
- `Admin/` - Componentes del panel de administracion
|
||||
- `Public/` - Componentes del frontend publico
|
||||
- `Shared/` - Codigo compartido entre contextos
|
||||
- **AND** ejemplos incorrectos: `backend/`, `frontend/`, `common/`
|
||||
|
||||
#### Scenario: Carpetas de modulo (componente)
|
||||
- **WHEN** se nombra una carpeta que representa un modulo/componente
|
||||
- **THEN** DEBE seguir PascalCase
|
||||
- **AND** DEBE coincidir con component_name convertido de kebab-case
|
||||
- **AND** ejemplos correctos:
|
||||
- `ContactForm/` (de `contact-form`)
|
||||
- `FeaturedImage/` (de `featured-image`)
|
||||
- `TopNotificationBar/` (de `top-notification-bar`)
|
||||
- `CtaBoxSidebar/` (de `cta-box-sidebar`)
|
||||
- **AND** ejemplos incorrectos:
|
||||
- `contact-form/` (kebab-case)
|
||||
- `contactForm/` (camelCase)
|
||||
- `CONTACT_FORM/` (UPPER_SNAKE)
|
||||
|
||||
#### Scenario: Carpetas de capa (Clean Architecture)
|
||||
- **WHEN** se nombra una carpeta que representa una capa de arquitectura
|
||||
- **THEN** DEBE usar PascalCase
|
||||
- **AND** carpetas permitidas dentro de cada modulo:
|
||||
- `Domain/` - Entidades, interfaces, value objects
|
||||
- `Application/` - Casos de uso
|
||||
- `Infrastructure/` - Implementaciones (Ui, Api, Persistence, Services)
|
||||
- **AND** subcarpetas de Infrastructure permitidas:
|
||||
- `Ui/` - Renderers, FormBuilders
|
||||
- `Api/` - Handlers AJAX, REST endpoints
|
||||
- `Persistence/` - Repositorios
|
||||
- `Services/` - Servicios de infraestructura
|
||||
- `WordPress/` - Integraciones especificas de WordPress
|
||||
- **AND** ejemplos correctos: `Infrastructure/Ui/`, `Infrastructure/Api/WordPress/`
|
||||
- **AND** ejemplos incorrectos: `infrastructure/`, `UI/`, `api/`
|
||||
|
||||
#### Scenario: Carpetas auxiliares (no codigo)
|
||||
- **WHEN** se nombra una carpeta de documentacion, planificacion o configuracion
|
||||
- **THEN** DEBE usar prefijo guion bajo + minusculas
|
||||
- **AND** ejemplos correctos:
|
||||
- `_planificacion/` - Documentos de planificacion
|
||||
- `_arquitectura/` - Documentos de arquitectura
|
||||
- **AND** nombres compuestos usan guion medio: `_pruebas-regresion/`
|
||||
- **AND** el guion bajo indica carpetas auxiliares/no-codigo
|
||||
|
||||
#### Scenario: Carpetas de cambios (OpenSpec)
|
||||
- **WHEN** se crea una carpeta para un cambio/feature en `_openspec/changes/`
|
||||
- **THEN** DEBE usar nombre en kebab-case descriptivo
|
||||
- **AND** maximo 1 nivel de carpeta despues de `changes/`
|
||||
- **AND** ejemplos correctos:
|
||||
- `anti-spam-validator/`
|
||||
- `lazy-loading-images/`
|
||||
- `improved-caching/`
|
||||
- **AND** ejemplos incorrectos:
|
||||
- `AntiSpam/` (PascalCase)
|
||||
- `anti_spam/` (snake_case)
|
||||
- `anti-spam/subfolder/` (anidamiento prohibido)
|
||||
|
||||
#### Scenario: Excepciones permitidas
|
||||
- **WHEN** existen carpetas especiales del sistema
|
||||
- **THEN** se permiten las siguientes excepciones:
|
||||
- `.git/` - Control de versiones
|
||||
- `.serena/` - Configuracion de Serena MCP
|
||||
- `.claude/` - Configuracion de Claude Code
|
||||
- `node_modules/` - Dependencias npm (si aplica)
|
||||
- `vendor/` - Dependencias Composer (si aplica)
|
||||
- `_openspec/` - Sistema de especificaciones (carpeta auxiliar)
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Nomenclatura de Archivos PHP
|
||||
|
||||
Los archivos PHP DEBEN seguir convencion PascalCase.
|
||||
|
||||
#### Scenario: Archivos de clase
|
||||
- **WHEN** se nombra un archivo que contiene una clase PHP
|
||||
- **THEN** DEBE seguir PascalCase
|
||||
- **AND** el nombre DEBE coincidir EXACTAMENTE con el nombre de la clase
|
||||
- **AND** extension `.php`
|
||||
- **AND** ejemplos correctos:
|
||||
- `ContactFormRenderer.php` (contiene `class ContactFormRenderer`)
|
||||
- `NewsletterAjaxHandler.php` (contiene `class NewsletterAjaxHandler`)
|
||||
- `CSSGeneratorService.php` (contiene `class CSSGeneratorService`)
|
||||
- **AND** ejemplos incorrectos:
|
||||
- `contact-form-renderer.php` (kebab-case)
|
||||
- `contactFormRenderer.php` (camelCase)
|
||||
- `class_contact_form.php` (snake_case con prefijo)
|
||||
|
||||
#### Scenario: Archivos de interface
|
||||
- **WHEN** se nombra un archivo que contiene una interface
|
||||
- **THEN** DEBE seguir PascalCase con sufijo Interface
|
||||
- **AND** extension `.php`
|
||||
- **AND** ejemplos correctos:
|
||||
- `RendererInterface.php`
|
||||
- `CSSGeneratorInterface.php`
|
||||
- `ComponentRepositoryInterface.php`
|
||||
- **AND** ejemplos incorrectos:
|
||||
- `IRenderer.php` (prefijo I estilo C#)
|
||||
- `Renderer.php` (sin sufijo)
|
||||
|
||||
#### Scenario: Archivos de trait
|
||||
- **WHEN** se nombra un archivo que contiene un trait PHP
|
||||
- **THEN** DEBE seguir PascalCase con sufijo Trait
|
||||
- **AND** extension `.php`
|
||||
- **AND** ejemplos correctos:
|
||||
- `VisibilityTrait.php`
|
||||
- `CSSGeneratorTrait.php`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Nomenclatura de Archivos JSON (Schemas)
|
||||
|
||||
Los schemas JSON DEBEN usar kebab-case.
|
||||
|
||||
#### Scenario: Archivos de schema de componente
|
||||
- **WHEN** se nombra un archivo JSON schema
|
||||
- **THEN** DEBE usar kebab-case
|
||||
- **AND** extension `.json`
|
||||
- **AND** el nombre DEBE coincidir con el component_name
|
||||
- **AND** ejemplos correctos:
|
||||
- `contact-form.json` (component_name: "contact-form")
|
||||
- `featured-image.json` (component_name: "featured-image")
|
||||
- `top-notification-bar.json` (component_name: "top-notification-bar")
|
||||
- `cta-box-sidebar.json` (component_name: "cta-box-sidebar")
|
||||
- **AND** ejemplos incorrectos:
|
||||
- `ContactForm.json` (PascalCase)
|
||||
- `contact_form.json` (snake_case)
|
||||
- `contactForm.json` (camelCase)
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Nomenclatura de Namespaces
|
||||
|
||||
Los namespaces PHP DEBEN seguir convencion PascalCase jerarquica.
|
||||
|
||||
#### Scenario: Namespace raiz
|
||||
- **WHEN** se define el namespace principal del tema
|
||||
- **THEN** DEBE ser `ROITheme`
|
||||
- **AND** ejemplo: `namespace ROITheme;`
|
||||
|
||||
#### Scenario: Namespaces de modulo publico
|
||||
- **WHEN** se define un namespace para un componente en Public/
|
||||
- **THEN** DEBE seguir `ROITheme\Public\[Componente]\[Capa][\Subcapa]`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
// Renderers
|
||||
namespace ROITheme\Public\ContactForm\Infrastructure\Ui;
|
||||
namespace ROITheme\Public\FeaturedImage\Infrastructure\Ui;
|
||||
namespace ROITheme\Public\TopNotificationBar\Infrastructure\Ui;
|
||||
|
||||
// AJAX Handlers
|
||||
namespace ROITheme\Public\Footer\Infrastructure\Api\WordPress;
|
||||
namespace ROITheme\Public\ContactForm\Infrastructure\Api\WordPress;
|
||||
|
||||
// Domain (si existe)
|
||||
namespace ROITheme\Public\AdsensePlacement\Domain\ValueObjects;
|
||||
namespace ROITheme\Public\AdsensePlacement\Domain\Contracts;
|
||||
|
||||
// Application (si existe)
|
||||
namespace ROITheme\Public\CustomCSSManager\Application\UseCases;
|
||||
```
|
||||
|
||||
#### Scenario: Namespaces de modulo admin
|
||||
- **WHEN** se define un namespace para un componente en Admin/
|
||||
- **THEN** DEBE seguir `ROITheme\Admin\[Componente]\[Capa][\Subcapa]`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
// FormBuilders
|
||||
namespace ROITheme\Admin\ContactForm\Infrastructure\Ui;
|
||||
namespace ROITheme\Admin\FeaturedImage\Infrastructure\Ui;
|
||||
|
||||
// Shared de Admin
|
||||
namespace ROITheme\Admin\Shared\Infrastructure\Ui;
|
||||
```
|
||||
|
||||
#### Scenario: Namespaces de Shared
|
||||
- **WHEN** se define un namespace para codigo compartido
|
||||
- **THEN** DEBE seguir `ROITheme\Shared\[Capa][\Subcapa]`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
// Domain
|
||||
namespace ROITheme\Shared\Domain\Contracts;
|
||||
namespace ROITheme\Shared\Domain\Entities;
|
||||
namespace ROITheme\Shared\Domain\ValueObjects;
|
||||
|
||||
// Application
|
||||
namespace ROITheme\Shared\Application\UseCases;
|
||||
|
||||
// Infrastructure
|
||||
namespace ROITheme\Shared\Infrastructure\Services;
|
||||
namespace ROITheme\Shared\Infrastructure\Persistence\WordPress;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Nomenclatura de Clases
|
||||
|
||||
Los nombres de clase DEBEN seguir convencion PascalCase.
|
||||
|
||||
#### Scenario: Clases regulares
|
||||
- **WHEN** se define una clase en PHP
|
||||
- **THEN** el nombre DEBE seguir PascalCase
|
||||
- **AND** DEBE ser un sustantivo o frase sustantiva
|
||||
- **AND** DEBE ser descriptivo de su responsabilidad
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
final class ContactFormRenderer
|
||||
final class ComponentSettings
|
||||
final class VisibilityChecker
|
||||
```
|
||||
- **AND** ejemplos incorrectos:
|
||||
```php
|
||||
final class contactFormRenderer // camelCase
|
||||
final class contact_form_renderer // snake_case
|
||||
final class Render // verbo, no sustantivo
|
||||
```
|
||||
|
||||
#### Scenario: Clases Renderer
|
||||
- **WHEN** se define una clase que renderiza HTML de un componente
|
||||
- **THEN** DEBE usar sufijo `Renderer`
|
||||
- **AND** DEBE implementar `RendererInterface`
|
||||
- **AND** DEBE ser `final`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
final class ContactFormRenderer implements RendererInterface
|
||||
final class FeaturedImageRenderer implements RendererInterface
|
||||
final class TopNotificationBarRenderer implements RendererInterface
|
||||
```
|
||||
|
||||
#### Scenario: Clases FormBuilder
|
||||
- **WHEN** se define una clase que genera formularios admin
|
||||
- **THEN** DEBE usar sufijo `FormBuilder`
|
||||
- **AND** DEBE ser `final`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
final class ContactFormFormBuilder
|
||||
final class FeaturedImageFormBuilder
|
||||
final class TopNotificationBarFormBuilder
|
||||
```
|
||||
|
||||
#### Scenario: Clases de caso de uso
|
||||
- **WHEN** se define una clase UseCase en Application/
|
||||
- **THEN** DEBE usar sufijo `UseCase`
|
||||
- **AND** el nombre DEBE describir la accion
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
final class GetCriticalCSSUseCase
|
||||
final class CheckAdsenseVisibilityUseCase
|
||||
final class GetDeferredSnippetsUseCase
|
||||
```
|
||||
|
||||
#### Scenario: Clases de servicio
|
||||
- **WHEN** se define una clase que provee servicios en Infrastructure/
|
||||
- **THEN** DEBE usar sufijo `Service`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
final class CSSGeneratorService
|
||||
final class AntiSpamValidatorService
|
||||
final class CacheService
|
||||
```
|
||||
|
||||
#### Scenario: Clases de repositorio
|
||||
- **WHEN** se define una clase de acceso a datos
|
||||
- **THEN** DEBE usar sufijo `Repository`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
final class ComponentSettingsRepository
|
||||
final class PageVisibilityRepository
|
||||
```
|
||||
|
||||
#### Scenario: Clases Handler (AJAX/API)
|
||||
- **WHEN** se define una clase que maneja peticiones AJAX o API
|
||||
- **THEN** DEBE usar sufijo `Handler` o `Controller`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
final class NewsletterAjaxHandler
|
||||
final class ContactFormAjaxHandler
|
||||
final class AdsenseVisibilityController
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Nomenclatura de Interfaces
|
||||
|
||||
Los nombres de interface DEBEN seguir convencion PascalCase con sufijo.
|
||||
|
||||
#### Scenario: Interfaces
|
||||
- **WHEN** se define una interface en PHP
|
||||
- **THEN** el nombre DEBE terminar con `Interface`
|
||||
- **AND** DEBE seguir PascalCase
|
||||
- **AND** DEBE describir la capacidad o contrato
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
interface RendererInterface
|
||||
interface CSSGeneratorInterface
|
||||
interface ComponentRepositoryInterface
|
||||
interface AjaxControllerInterface
|
||||
```
|
||||
- **AND** ejemplos incorrectos:
|
||||
```php
|
||||
interface IRenderer // prefijo I estilo C#
|
||||
interface Renderer // sin sufijo
|
||||
interface renderer_interface // snake_case
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Nomenclatura de Metodos
|
||||
|
||||
Los nombres de metodo DEBEN seguir convencion camelCase.
|
||||
|
||||
#### Scenario: Metodos publicos
|
||||
- **WHEN** se define un metodo publico
|
||||
- **THEN** el nombre DEBE seguir camelCase
|
||||
- **AND** DEBE comenzar con verbo que describe la accion
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
public function render(Component $component): string
|
||||
public function getVisibilityClass(array $data): ?string
|
||||
public function validateInput(string $input): bool
|
||||
public function buildForm(string $componentId): string
|
||||
```
|
||||
- **AND** ejemplos incorrectos:
|
||||
```php
|
||||
public function Render() // PascalCase
|
||||
public function get_visibility_class() // snake_case
|
||||
public function visibility() // sin verbo
|
||||
```
|
||||
|
||||
#### Scenario: Metodos privados
|
||||
- **WHEN** se define un metodo privado
|
||||
- **THEN** DEBE seguir camelCase (igual que publicos)
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
private function parseResponse(): array
|
||||
private function validateInternal(): bool
|
||||
private function generateCSS(array $data): string
|
||||
```
|
||||
|
||||
#### Scenario: Metodos booleanos
|
||||
- **WHEN** un metodo retorna un valor booleano
|
||||
- **THEN** DEBE usar prefijo `is`, `has`, `can`, `should`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
public function isEnabled(array $data): bool
|
||||
public function hasPermission(string $capability): bool
|
||||
public function canProcess(): bool
|
||||
public function shouldShow(string $component): bool
|
||||
```
|
||||
- **AND** ejemplos incorrectos:
|
||||
```php
|
||||
public function enabled(): bool // sin prefijo
|
||||
public function checkEnabled(): bool // check no es booleano
|
||||
public function getIsEnabled(): bool // get redundante
|
||||
```
|
||||
|
||||
#### Scenario: Metodos getter/setter
|
||||
- **WHEN** se define un metodo de acceso
|
||||
- **THEN** getters DEBEN usar prefijo `get`
|
||||
- **AND** setters DEBEN usar prefijo `set`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
public function getData(): array
|
||||
public function setData(array $data): void
|
||||
public function getComponentName(): string
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Nomenclatura de Propiedades y Variables
|
||||
|
||||
Las propiedades y variables DEBEN seguir convencion camelCase.
|
||||
|
||||
#### Scenario: Propiedades de clase
|
||||
- **WHEN** se declara una propiedad de clase
|
||||
- **THEN** DEBE seguir camelCase
|
||||
- **AND** DEBE tener visibilidad explicita (private, protected, public)
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
private CSSGeneratorInterface $cssGenerator;
|
||||
private string $componentName;
|
||||
protected array $settings;
|
||||
```
|
||||
- **AND** ejemplos incorrectos:
|
||||
```php
|
||||
private $CssGenerator; // PascalCase
|
||||
private $css_generator; // snake_case
|
||||
private $_cssGenerator; // prefijo _ (no necesario en PHP moderno)
|
||||
```
|
||||
|
||||
#### Scenario: Propiedades booleanas
|
||||
- **WHEN** se define una propiedad booleana
|
||||
- **THEN** DEBE usar prefijo `is`, `has`, `can`
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
private bool $isEnabled;
|
||||
private bool $hasChanges;
|
||||
private bool $canEdit;
|
||||
```
|
||||
|
||||
#### Scenario: Variables locales
|
||||
- **WHEN** se declara una variable local
|
||||
- **THEN** DEBE seguir $camelCase
|
||||
- **AND** DEBE ser descriptiva
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
$showDesktop = $data['visibility']['show_on_desktop'] ?? true;
|
||||
$visibilityClass = $this->getVisibilityClass($data);
|
||||
$componentSettings = $this->repository->get($componentName);
|
||||
```
|
||||
- **AND** ejemplos incorrectos:
|
||||
```php
|
||||
$ShowDesktop // PascalCase
|
||||
$show_desktop // snake_case
|
||||
$sd // abreviatura
|
||||
$strShowDesktop // notacion hungara
|
||||
```
|
||||
|
||||
#### Scenario: Parametros de metodo
|
||||
- **WHEN** se declara un parametro de metodo
|
||||
- **THEN** DEBE seguir $camelCase
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
public function render(Component $component): string
|
||||
public function validateField(string $fieldName, mixed $value): bool
|
||||
```
|
||||
|
||||
#### Scenario: Variables de iteracion
|
||||
- **WHEN** se usa una variable de iteracion en bucle
|
||||
- **THEN** se permiten nombres cortos para indices: `$i`, `$j`, `$k`
|
||||
- **AND** se prefiere nombre descriptivo para elementos
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
for ($i = 0; $i < count($items); $i++)
|
||||
foreach ($components as $component)
|
||||
foreach ($settings as $key => $value)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Nomenclatura de Constantes
|
||||
|
||||
Las constantes DEBEN seguir convencion UPPER_SNAKE_CASE.
|
||||
|
||||
#### Scenario: Constantes de clase
|
||||
- **WHEN** se define una constante de clase
|
||||
- **THEN** DEBE seguir UPPER_SNAKE_CASE
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
private const COMPONENT_NAME = 'contact-form';
|
||||
public const MAX_RETRY_COUNT = 3;
|
||||
protected const DEFAULT_TIMEOUT = 5000;
|
||||
```
|
||||
- **AND** ejemplos incorrectos:
|
||||
```php
|
||||
private const componentName = 'contact-form'; // camelCase
|
||||
private const ComponentName = 'contact-form'; // PascalCase
|
||||
```
|
||||
|
||||
#### Scenario: Constantes globales
|
||||
- **WHEN** se define una constante global del tema
|
||||
- **THEN** DEBE usar prefijo `ROI_THEME_`
|
||||
- **AND** DEBE seguir UPPER_SNAKE_CASE
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
define('ROI_THEME_VERSION', '1.0.0');
|
||||
define('ROI_THEME_PATH', get_template_directory());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Nomenclatura de component_name
|
||||
|
||||
El identificador de componente DEBE usar kebab-case.
|
||||
|
||||
#### Scenario: component_name en JSON
|
||||
- **WHEN** se define component_name en un schema JSON
|
||||
- **THEN** DEBE usar kebab-case
|
||||
- **AND** DEBE coincidir con el nombre del archivo JSON
|
||||
- **AND** ejemplos correctos:
|
||||
```json
|
||||
{
|
||||
"component_name": "contact-form",
|
||||
"component_name": "featured-image",
|
||||
"component_name": "top-notification-bar",
|
||||
"component_name": "cta-box-sidebar"
|
||||
}
|
||||
```
|
||||
|
||||
#### Scenario: component_name en BD
|
||||
- **WHEN** se guarda component_name en base de datos
|
||||
- **THEN** DEBE mantener kebab-case
|
||||
- **AND** tabla: `wp_roi_theme_component_settings`
|
||||
- **AND** columna: `component_name`
|
||||
|
||||
#### Scenario: component_name en codigo PHP
|
||||
- **WHEN** se usa component_name en codigo PHP
|
||||
- **THEN** DEBE mantenerse en kebab-case
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
private const COMPONENT_NAME = 'contact-form';
|
||||
|
||||
public function supports(string $componentType): bool
|
||||
{
|
||||
return $componentType === 'contact-form';
|
||||
}
|
||||
|
||||
// En data-attribute
|
||||
$html .= 'data-component="contact-form"';
|
||||
```
|
||||
|
||||
#### Scenario: Conversion kebab-case a PascalCase
|
||||
- **WHEN** se necesita convertir component_name a nombre de carpeta/clase
|
||||
- **THEN** eliminar guiones y capitalizar cada palabra
|
||||
- **AND** ejemplos de conversion:
|
||||
| kebab-case | PascalCase |
|
||||
|------------|------------|
|
||||
| `contact-form` | `ContactForm` |
|
||||
| `featured-image` | `FeaturedImage` |
|
||||
| `top-notification-bar` | `TopNotificationBar` |
|
||||
| `cta-box-sidebar` | `CtaBoxSidebar` |
|
||||
| `cta-lets-talk` | `CtaLetsTalk` |
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Nomenclatura de Hooks WordPress
|
||||
|
||||
Los hooks DEBEN usar snake_case con prefijo del tema.
|
||||
|
||||
#### Scenario: Actions del tema
|
||||
- **WHEN** se define un action hook del tema
|
||||
- **THEN** DEBE usar prefijo `roi_theme_`
|
||||
- **AND** DEBE seguir snake_case
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
do_action('roi_theme_after_render', $component);
|
||||
do_action('roi_theme_before_form_submit');
|
||||
do_action('roi_theme_component_loaded', $componentName);
|
||||
```
|
||||
|
||||
#### Scenario: Filters del tema
|
||||
- **WHEN** se define un filter hook del tema
|
||||
- **THEN** DEBE usar prefijo `roi_theme_filter_`
|
||||
- **AND** DEBE seguir snake_case
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
$css = apply_filters('roi_theme_filter_component_css', $css, $component);
|
||||
$html = apply_filters('roi_theme_filter_render_output', $html);
|
||||
```
|
||||
|
||||
#### Scenario: Acciones AJAX
|
||||
- **WHEN** se registra una accion AJAX
|
||||
- **THEN** DEBE usar prefijo `roi_`
|
||||
- **AND** DEBE seguir snake_case
|
||||
- **AND** ejemplos correctos:
|
||||
```php
|
||||
add_action('wp_ajax_roi_newsletter_subscribe', [$this, 'handle']);
|
||||
add_action('wp_ajax_nopriv_roi_contact_form_submit', [$this, 'handle']);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Prohibicion de Notacion Hungara
|
||||
|
||||
La notacion hungara esta PROHIBIDA.
|
||||
|
||||
#### Scenario: Prefijos de tipo prohibidos
|
||||
- **WHEN** se nombra una variable, propiedad o parametro
|
||||
- **THEN** NO DEBE usar prefijos de tipo
|
||||
- **AND** prefijos prohibidos:
|
||||
|
||||
| Prefijo | Significado | Ejemplo Incorrecto |
|
||||
|---------|-------------|-------------------|
|
||||
| `str` | String | `$strNombre` |
|
||||
| `int`, `i` | Integer | `$intContador`, `$iTotal` |
|
||||
| `b`, `bln` | Boolean | `$bActivo`, `$blnValido` |
|
||||
| `arr` | Array | `$arrItems` |
|
||||
| `obj` | Object | `$objRenderer` |
|
||||
|
||||
#### Scenario: Nombres correctos sin notacion hungara
|
||||
- **WHEN** se reemplaza notacion hungara
|
||||
- **THEN** usar nombres descriptivos
|
||||
|
||||
| Incorrecto | Correcto |
|
||||
|------------|----------|
|
||||
| `$strNombre` | `$name` o `$customerName` |
|
||||
| `$bActivo` | `$isActive` |
|
||||
| `$arrItems` | `$items` |
|
||||
| `$objRenderer` | `$renderer` |
|
||||
| `$intCount` | `$count` o `$itemCount` |
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Validacion Pre-Commit de Nomenclatura
|
||||
|
||||
Las convenciones DEBEN validarse antes del commit.
|
||||
|
||||
#### Scenario: Checklist de nomenclatura
|
||||
- **WHEN** el codigo esta listo para commit
|
||||
- **THEN** verificar:
|
||||
- [ ] Carpetas de modulo en PascalCase
|
||||
- [ ] Archivos PHP en PascalCase.php
|
||||
- [ ] Archivos JSON schema en kebab-case.json
|
||||
- [ ] Namespaces en PascalCase jerarquico
|
||||
- [ ] Clases en PascalCase
|
||||
- [ ] Interfaces con sufijo Interface
|
||||
- [ ] Metodos en camelCase
|
||||
- [ ] Propiedades en camelCase
|
||||
- [ ] Variables locales en $camelCase
|
||||
- [ ] Constantes en UPPER_SNAKE_CASE
|
||||
- [ ] component_name en kebab-case
|
||||
- [ ] Hooks con prefijo roi_theme_
|
||||
- [ ] SIN notacion hungara
|
||||
- [ ] Metodos booleanos con is/has/can/should
|
||||
- [ ] Renderers con sufijo Renderer
|
||||
- [ ] FormBuilders con sufijo FormBuilder
|
||||
- [ ] UseCases con sufijo UseCase
|
||||
- [ ] Services con sufijo Service
|
||||
|
||||
---
|
||||
|
||||
## Tabla de Conversion Rapida
|
||||
|
||||
| De | A | Ejemplo |
|
||||
|----|---|---------|
|
||||
| kebab-case | PascalCase | `contact-form` → `ContactForm` |
|
||||
| PascalCase | kebab-case | `ContactForm` → `contact-form` |
|
||||
| PascalCase | camelCase | `ContactForm` → `contactForm` |
|
||||
| snake_case | PascalCase | `contact_form` → `ContactForm` |
|
||||
| snake_case | camelCase | `contact_form` → `contactForm` |
|
||||
|
||||
---
|
||||
|
||||
**Última actualización:** 2026-01-08
|
||||
Reference in New Issue
Block a user