Files
roi-theme/_openspec/specs/arquitectura-limpia.md
FrankZamora 0f6387ab46 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>
2026-01-08 15:30:45 -06:00

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.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
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