- 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>
33 KiB
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.mdNOTA: Para principios SOLID y estandares de codigo, ver_openspec/specs/estandares-codigo.mdNOTA: 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, presentacionApi/- Handlers AJAX, REST endpointsApi/WordPress/- Handlers especificos de WordPressPersistence/- Repositorios genericosPersistence/WordPress/- Repositorios WordPressServices/- 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
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
// ❌ 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
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
// ❌ 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
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
// ❌ 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
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
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
# 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