diff --git a/admin/README.md b/admin/README.md new file mode 100644 index 00000000..027a8218 --- /dev/null +++ b/admin/README.md @@ -0,0 +1,257 @@ +# Contexto Admin - Administración de Componentes + +## Propósito + +El contexto `admin/` contiene **todo el código relacionado con la administración de componentes** en el panel +de WordPress. Cada componente tiene su propia carpeta con su Clean Architecture completa. + +## Filosofía: Context-First Architecture + +En lugar de separar por capas (Domain, Application, Infrastructure), separamos por **contextos** +(admin, public, shared) y cada contexto tiene sus propias capas internas. + +## Estructura (Fase-00) + +En Fase-00 solo creamos la estructura base. Los componentes se crearán en fases posteriores: + +``` +admin/ +├── README.md (este archivo) +└── .gitkeep (preserva directorio en Git) +``` + +## Estructura Futura (Post Fase-00) + +``` +admin/ +├── Navbar/ # Componente Navbar +│ ├── Domain/ +│ │ ├── NavbarComponent.php # Entidad +│ │ └── NavbarRepositoryInterface.php +│ ├── Application/ +│ │ ├── SaveNavbarUseCase.php # Caso de uso +│ │ └── DTO/ +│ │ └── NavbarSettingsDTO.php +│ ├── Infrastructure/ +│ │ ├── Persistence/ +│ │ │ └── WordPressNavbarRepository.php +│ │ ├── UI/ +│ │ │ ├── NavbarAdminPage.php # Página de admin +│ │ │ ├── NavbarForm.php # Formulario +│ │ │ └── views/ +│ │ │ └── navbar-settings.php +│ │ └── API/ +│ │ └── NavbarAjaxHandler.php # AJAX endpoints +│ └── README.md +├── Footer/ # Otro componente +│ └── (misma estructura) +└── (más componentes...) +``` + +## Principios del Contexto Admin + +1. **Aislamiento**: Cada componente es independiente +2. **Clean Architecture**: Cada componente tiene Domain, Application, Infrastructure +3. **Sin acoplamiento**: Un componente no depende de otro directamente +4. **Código compartido**: Va en `shared/` si es usado por múltiples componentes + +## Responsabilidades + +El contexto `admin/` se encarga de: + +✅ **Formularios de configuración** de componentes +✅ **Validación de entrada** del usuario administrador +✅ **Guardado de configuraciones** en la base de datos +✅ **Páginas de administración** en el panel de WordPress +✅ **AJAX endpoints** para operaciones administrativas +✅ **Permisos y capabilities** de administrador + +❌ **NO** se encarga de: +- Renderizado frontend de componentes (va en `public/`) +- Lógica compartida entre contextos (va en `shared/`) +- Configuraciones globales del tema + +## Ejemplo de Flujo de Admin + +### 1. Usuario administra el Navbar + +``` +Usuario Admin (navegador) + ↓ +NavbarAdminPage.php (muestra formulario) + ↓ +Usuario envía formulario via AJAX + ↓ +NavbarAjaxHandler.php (recibe request) + ↓ +SaveNavbarUseCase.php (orquesta la lógica) + ↓ +WordPressNavbarRepository.php (guarda en DB) + ↓ +Respuesta JSON al navegador +``` + +### 2. Código de ejemplo + +```php +// admin/Navbar/Application/SaveNavbarUseCase.php +namespace ROITheme\Admin\Navbar\Application; + +use ROITheme\Admin\Navbar\Domain\NavbarRepositoryInterface; +use ROITheme\Shared\Application\Contracts\ValidationServiceInterface; + +final class SaveNavbarUseCase +{ + public function __construct( + private readonly NavbarRepositoryInterface $repository, + private readonly ValidationServiceInterface $validator + ) {} + + public function execute(array $data): void + { + $validated = $this->validator->validate($data, [ + 'logo_url' => 'url', + 'menu_items' => 'array', + 'sticky' => 'bool', + ]); + + if ($this->validator->fails()) { + throw new ValidationException($this->validator->errors()); + } + + $this->repository->save($validated); + } +} +``` + +```php +// admin/Navbar/Infrastructure/UI/NavbarAdminPage.php +namespace ROITheme\Admin\Navbar\Infrastructure\UI; + +final class NavbarAdminPage +{ + public function register(): void + { + add_action('admin_menu', [$this, 'addMenuPage']); + } + + public function addMenuPage(): void + { + add_menu_page( + 'Navbar Settings', + 'Navbar', + 'manage_options', + 'roi-navbar-settings', + [$this, 'render'], + 'dashicons-menu', + 30 + ); + } + + public function render(): void + { + require __DIR__ . '/views/navbar-settings.php'; + } +} +``` + +## Relación con Otros Contextos + +``` +admin/ → Administra configuraciones + ↓ guarda +Base de Datos → Almacena settings + ↓ lee +public/ → Renderiza componentes en frontend +``` + +**Clave**: `admin/` y `public/` NO se hablan directamente. Se comunican a través de la base de datos. + +## Reglas de Dependencia + +✅ **PUEDE** depender de: +- `shared/Domain/` (Value Objects, Exceptions) +- `shared/Application/` (Contracts, Services) +- `shared/Infrastructure/` (Implementaciones de servicios) +- WordPress admin functions (`add_menu_page`, `add_settings_section`, etc.) + +❌ **NO PUEDE** depender de: +- `public/` (contexto independiente) +- Código de frontend (JS/CSS va en Infrastructure/UI/) + +## Testing + +### Tests Unitarios +```php +// tests/Unit/Admin/Navbar/Application/SaveNavbarUseCaseTest.php +public function test_saves_valid_navbar_settings() +{ + $repository = $this->createMock(NavbarRepositoryInterface::class); + $validator = $this->createMock(ValidationServiceInterface::class); + + $useCase = new SaveNavbarUseCase($repository, $validator); + $useCase->execute(['logo_url' => 'http://example.com/logo.png']); + + // Assertions... +} +``` + +### Tests de Integración +```php +// tests/Integration/Admin/Navbar/Infrastructure/WordPressNavbarRepositoryTest.php +public function test_saves_and_retrieves_navbar_settings() +{ + $repository = new WordPressNavbarRepository(); + $settings = ['logo_url' => 'http://example.com/logo.png']; + + $repository->save($settings); + $retrieved = $repository->get(); + + $this->assertEquals($settings, $retrieved); +} +``` + +### Tests E2E (Playwright) +```php +// tests/E2E/Admin/NavbarAdminPageTest.php +public function test_admin_can_save_navbar_settings() +{ + $this->loginAsAdmin(); + $this->visit('/wp-admin/admin.php?page=roi-navbar-settings'); + $this->fillField('logo_url', 'http://example.com/logo.png'); + $this->click('Save Settings'); + $this->see('Settings saved successfully'); +} +``` + +## Cuándo Agregar Código Aquí + +Agrega código a `admin/` cuando: +- Creas un nuevo componente administrable +- Necesitas una página de configuración en el panel de WordPress +- Manejas formularios de administrador +- Procesas AJAX desde el admin +- Validas o guardas configuraciones + +No agregues aquí: +- Renderizado frontend (va en `public/`) +- Lógica compartida (va en `shared/`) +- Configuraciones globales del tema + +## Estado Actual (Fase-00) + +En Fase-00, `admin/` solo tiene la estructura base. Los componentes se crearán en las siguientes fases: + +- **Fase-1**: Estructura base e infraestructura +- **Fase-2**: Migración de base de datos +- **Fase-3**: Implementación de componentes admin +- **Fase-4+**: Componentes adicionales + +## Próximos Pasos + +1. Crear primer componente en Fase-3 (ej: Navbar) +2. Implementar Domain layer del componente +3. Implementar Application layer (casos de uso) +4. Implementar Infrastructure layer (WordPress integration) +5. Crear tests unitarios e integración +6. Repetir para cada componente diff --git a/public/README.md b/public/README.md new file mode 100644 index 00000000..1501f1f3 --- /dev/null +++ b/public/README.md @@ -0,0 +1,393 @@ +# Contexto Public - Renderizado Frontend de Componentes + +## Propósito + +El contexto `public/` contiene **todo el código relacionado con el renderizado de componentes en el frontend**. +Cada componente tiene su propia carpeta con su Clean Architecture completa. + +## Filosofía: Context-First Architecture + +Similar a `admin/`, el contexto `public/` agrupa todo lo relacionado con la visualización pública de componentes. +Cada componente es independiente y tiene sus propias capas de Clean Architecture. + +## Estructura (Fase-00) + +En Fase-00 solo creamos la estructura base. Los componentes se crearán en fases posteriores: + +``` +public/ +├── README.md (este archivo) +└── .gitkeep (preserva directorio en Git) +``` + +## Estructura Futura (Post Fase-00) + +``` +public/ +├── Navbar/ # Componente Navbar +│ ├── Domain/ +│ │ ├── NavbarData.php # Entidad de lectura +│ │ └── NavbarDataProviderInterface.php +│ ├── Application/ +│ │ ├── GetNavbarDataUseCase.php # Caso de uso +│ │ └── DTO/ +│ │ └── NavbarDataDTO.php +│ ├── Infrastructure/ +│ │ ├── Persistence/ +│ │ │ └── WordPressNavbarDataProvider.php +│ │ ├── UI/ +│ │ │ ├── NavbarRenderer.php # Renderizador +│ │ │ ├── views/ +│ │ │ │ └── navbar.php # Template +│ │ │ └── assets/ +│ │ │ ├── css/ +│ │ │ │ └── navbar.css +│ │ │ └── js/ +│ │ │ └── navbar.js +│ │ └── Hooks/ +│ │ └── NavbarHooks.php # WordPress hooks +│ └── README.md +├── Footer/ # Otro componente +│ └── (misma estructura) +└── (más componentes...) +``` + +## Principios del Contexto Public + +1. **Solo lectura**: El frontend solo lee configuraciones, no las modifica +2. **Performance**: Optimizado para carga rápida y caché +3. **Presentación**: Se enfoca en renderizar HTML, CSS, JS +4. **Independencia**: Cada componente es autónomo + +## Responsabilidades + +El contexto `public/` se encarga de: + +✅ **Renderizado de componentes** en el frontend +✅ **Lectura de configuraciones** de la base de datos +✅ **Generación de HTML** según templates +✅ **Carga de assets** (CSS, JS) del componente +✅ **Enqueue de scripts y estilos** +✅ **Hooks de WordPress** para frontend (`wp_head`, `wp_footer`, etc.) + +❌ **NO** se encarga de: +- Administración de componentes (va en `admin/`) +- Guardado de configuraciones +- Lógica compartida (va en `shared/`) + +## Ejemplo de Flujo de Public + +### 1. Usuario visita página web + +``` +Usuario (navegador) + ↓ +WordPress carga tema + ↓ +NavbarHooks.php (registra hook 'wp_body_open') + ↓ +Hook ejecutado + ↓ +NavbarRenderer.php (obtiene datos y renderiza) + ↓ +GetNavbarDataUseCase.php (obtiene configuración) + ↓ +WordPressNavbarDataProvider.php (lee de DB) + ↓ +navbar.php (template con HTML) + ↓ +HTML enviado al navegador +``` + +### 2. Código de ejemplo + +```php +// public/Navbar/Application/GetNavbarDataUseCase.php +namespace ROITheme\Public\Navbar\Application; + +use ROITheme\Public\Navbar\Domain\NavbarDataProviderInterface; + +final class GetNavbarDataUseCase +{ + public function __construct( + private readonly NavbarDataProviderInterface $dataProvider + ) {} + + public function execute(): array + { + $data = $this->dataProvider->get(); + + // Transformar datos si es necesario + return [ + 'logo_url' => $data['logo_url'] ?? '', + 'menu_items' => $data['menu_items'] ?? [], + 'sticky' => $data['sticky'] ?? false, + ]; + } +} +``` + +```php +// public/Navbar/Infrastructure/UI/NavbarRenderer.php +namespace ROITheme\Public\Navbar\Infrastructure\UI; + +use ROITheme\Public\Navbar\Application\GetNavbarDataUseCase; + +final class NavbarRenderer +{ + public function __construct( + private readonly GetNavbarDataUseCase $getNavbarData + ) {} + + public function render(): void + { + $data = $this->getNavbarData->execute(); + + // Encolar assets + wp_enqueue_style( + 'roi-navbar', + get_template_directory_uri() . '/public/Navbar/Infrastructure/UI/assets/css/navbar.css', + [], + '1.0.0' + ); + + wp_enqueue_script( + 'roi-navbar', + get_template_directory_uri() . '/public/Navbar/Infrastructure/UI/assets/js/navbar.js', + ['jquery'], + '1.0.0', + true + ); + + // Renderizar template + require __DIR__ . '/views/navbar.php'; + } +} +``` + +```php +// public/Navbar/Infrastructure/Hooks/NavbarHooks.php +namespace ROITheme\Public\Navbar\Infrastructure\Hooks; + +use ROITheme\Public\Navbar\Infrastructure\UI\NavbarRenderer; + +final class NavbarHooks +{ + public function __construct( + private readonly NavbarRenderer $renderer + ) {} + + public function register(): void + { + add_action('wp_body_open', [$this->renderer, 'render']); + } +} +``` + +```php +// public/Navbar/Infrastructure/UI/views/navbar.php + + +``` + +## Relación con Otros Contextos + +``` +admin/ → Guarda configuraciones + ↓ +Base de Datos → Almacena settings + ↓ lee +public/ → Renderiza componentes + ↓ +Navegador del Usuario +``` + +**Clave**: `public/` solo LECTURA, nunca escritura. + +## Optimización y Caché + +### 1. Usar Transients de WordPress + +```php +public function get(): array +{ + $cached = get_transient('roi_navbar_data'); + + if ($cached !== false) { + return $cached; + } + + $data = $this->fetchFromDatabase(); + set_transient('roi_navbar_data', $data, HOUR_IN_SECONDS); + + return $data; +} +``` + +### 2. Conditional Asset Loading + +```php +if ($data['sticky']) { + wp_enqueue_script('roi-navbar-sticky'); +} +``` + +### 3. Lazy Loading de Componentes + +```php +add_action('wp_body_open', function() { + if (is_front_page()) { + // Solo cargar en homepage + $renderer->render(); + } +}); +``` + +## Reglas de Dependencia + +✅ **PUEDE** depender de: +- `shared/Domain/` (Value Objects, Exceptions) +- `shared/Application/` (Contracts, Services) +- `shared/Infrastructure/` (Implementaciones de servicios) +- WordPress frontend functions (`wp_enqueue_style`, `wp_head`, etc.) + +❌ **NO PUEDE** depender de: +- `admin/` (contexto independiente) +- Funciones de administración de WordPress + +## Testing + +### Tests Unitarios +```php +// tests/Unit/Public/Navbar/Application/GetNavbarDataUseCaseTest.php +public function test_returns_navbar_data() +{ + $dataProvider = $this->createMock(NavbarDataProviderInterface::class); + $dataProvider->method('get')->willReturn(['logo_url' => 'test.png']); + + $useCase = new GetNavbarDataUseCase($dataProvider); + $data = $useCase->execute(); + + $this->assertEquals('test.png', $data['logo_url']); +} +``` + +### Tests de Integración +```php +// tests/Integration/Public/Navbar/Infrastructure/WordPressNavbarDataProviderTest.php +public function test_retrieves_navbar_settings_from_db() +{ + update_option('roi_navbar_settings', ['logo_url' => 'test.png']); + + $provider = new WordPressNavbarDataProvider(); + $data = $provider->get(); + + $this->assertEquals('test.png', $data['logo_url']); +} +``` + +### Tests E2E (Playwright) +```php +// tests/E2E/Public/NavbarRenderingTest.php +public function test_navbar_renders_on_homepage() +{ + $this->visit('/'); + $this->see('.roi-navbar'); + $this->see('Logo'); +} +``` + +### Tests de Performance +```php +// tests/Performance/Public/NavbarPerformanceTest.php +public function test_navbar_renders_in_less_than_100ms() +{ + $start = microtime(true); + + $renderer = new NavbarRenderer($this->getNavbarDataUseCase); + ob_start(); + $renderer->render(); + ob_end_clean(); + + $duration = (microtime(true) - $start) * 1000; + + $this->assertLessThan(100, $duration); +} +``` + +## Cuándo Agregar Código Aquí + +Agrega código a `public/` cuando: +- Renderizas un componente en el frontend +- Necesitas mostrar datos al usuario final +- Cargas assets (CSS, JS) para el frontend +- Registras hooks de frontend (`wp_head`, `wp_footer`, etc.) +- Optimizas performance de renderizado + +No agregues aquí: +- Formularios de admin (van en `admin/`) +- Guardado de configuraciones +- Lógica compartida (va en `shared/`) + +## Assets y Performance + +### Organización de Assets +``` +public/Navbar/Infrastructure/UI/assets/ +├── css/ +│ ├── navbar.css # Estilos del componente +│ └── navbar.min.css # Versión minificada +├── js/ +│ ├── navbar.js # JavaScript del componente +│ └── navbar.min.js # Versión minificada +└── images/ + └── default-logo.png # Imágenes del componente +``` + +### Minificación y Concatenación +En producción, usar versiones minificadas: +```php +$suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min'; +wp_enqueue_style('roi-navbar', "...navbar{$suffix}.css"); +``` + +## Estado Actual (Fase-00) + +En Fase-00, `public/` solo tiene la estructura base. Los componentes se crearán en las siguientes fases: + +- **Fase-1**: Estructura base e infraestructura +- **Fase-2**: Migración de base de datos +- **Fase-3**: Implementación de componentes públicos +- **Fase-4+**: Componentes adicionales y optimización + +## Próximos Pasos + +1. Crear primer componente en Fase-3 (ej: Navbar) +2. Implementar Domain layer (entidades de lectura) +3. Implementar Application layer (casos de uso de lectura) +4. Implementar Infrastructure layer (renderizado, hooks) +5. Crear templates y assets +6. Optimizar con caché +7. Crear tests de performance +8. Repetir para cada componente diff --git a/shared/Application/Contracts/.gitkeep b/shared/Application/Contracts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/Application/README.md b/shared/Application/README.md new file mode 100644 index 00000000..4cfa0a13 --- /dev/null +++ b/shared/Application/README.md @@ -0,0 +1,138 @@ +# Capa de Aplicación - Shared (Casos de Uso Compartidos) + +## Propósito + +La capa de Aplicación de `shared/` contiene **casos de uso y servicios compartidos** que orquestan la lógica +de dominio. Esta capa coordina el flujo de datos entre el dominio y la infraestructura. + +## Principios + +1. **Orquestación**: Coordina objetos de dominio para realizar casos de uso +2. **Sin lógica de negocio**: Delega la lógica al dominio +3. **Independiente de frameworks**: No depende de WordPress directamente +4. **Contratos claros**: Define interfaces para servicios + +## Estructura + +``` +shared/Application/ +├── Contracts/ # Interfaces de servicios (CacheServiceInterface, etc.) +└── Services/ # Implementaciones de servicios compartidos +``` + +## Ejemplos de Uso + +### Contracts (Interfaces de Servicios) + +```php +namespace ROITheme\Shared\Application\Contracts; + +interface CacheServiceInterface +{ + public function get(string $key): mixed; + public function set(string $key, mixed $value, int $ttl = 3600): bool; + public function delete(string $key): bool; + public function flush(): bool; +} +``` + +```php +namespace ROITheme\Shared\Application\Contracts; + +interface ValidationServiceInterface +{ + public function validate(array $data, array $rules): array; + public function fails(): bool; + public function errors(): array; +} +``` + +### Services (Implementaciones Abstractas) + +Servicios que pueden tener múltiples implementaciones: + +```php +namespace ROITheme\Shared\Application\Services; + +use ROITheme\Shared\Application\Contracts\ValidationServiceInterface; + +abstract class BaseValidationService implements ValidationServiceInterface +{ + protected array $errors = []; + + public function fails(): bool + { + return !empty($this->errors); + } + + public function errors(): array + { + return $this->errors; + } + + abstract public function validate(array $data, array $rules): array; +} +``` + +## Relación con Infrastructure + +La capa de Application **define contratos** (interfaces), pero **no implementa** la lógica específica +de frameworks. Las implementaciones concretas van en `shared/Infrastructure/`: + +``` +shared/Application/Contracts/CacheServiceInterface.php (interface) + ↓ +shared/Infrastructure/Services/WordPressCacheService.php (implementación) +``` + +## Reglas de Dependencia + +✅ **PUEDE** depender de: +- `shared/Domain/` (Value Objects, Entities, Exceptions) +- Interfaces dentro de `shared/Application/Contracts/` + +❌ **NO PUEDE** depender de: +- `shared/Infrastructure/` (implementaciones concretas) +- `admin/` o `public/` +- WordPress functions directamente +- Detalles de implementación + +## Testing + +Los servicios de esta capa se testean con **tests unitarios usando mocks**: + +```php +// tests/Unit/Shared/Application/Services/SomeServiceTest.php +public function test_validates_data_correctly() +{ + $validator = $this->createMock(ValidationServiceInterface::class); + $validator->method('validate')->willReturn(['name' => 'Test']); + + $service = new SomeService($validator); + $result = $service->process(['name' => 'Test']); + + $this->assertTrue($result); +} +``` + +## Cuándo Agregar Código Aquí + +Agrega código a `shared/Application/` cuando: +- Defines un contrato para un servicio compartido +- Necesitas orquestar múltiples objetos de dominio +- El servicio será usado por admin/ y public/ +- Necesitas abstraer implementaciones de infraestructura + +No agregues aquí: +- Lógica de negocio (va en Domain/) +- Implementaciones concretas de WordPress (van en Infrastructure/) +- Lógica específica de un solo contexto +- Código acoplado a frameworks + +## Ejemplos de Servicios Comunes + +- `ValidationServiceInterface`: Validación de datos +- `CacheServiceInterface`: Manejo de caché +- `SerializerServiceInterface`: Serialización de datos +- `LoggerServiceInterface`: Logging +- `EventDispatcherInterface`: Eventos del sistema diff --git a/shared/Application/Services/.gitkeep b/shared/Application/Services/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/Domain/Contracts/.gitkeep b/shared/Domain/Contracts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/Domain/Exceptions/.gitkeep b/shared/Domain/Exceptions/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/Domain/README.md b/shared/Domain/README.md new file mode 100644 index 00000000..d764ddc5 --- /dev/null +++ b/shared/Domain/README.md @@ -0,0 +1,127 @@ +# Capa de Dominio - Shared (Fundación) + +## Propósito + +La capa de Dominio de `shared/` contiene la **lógica de negocio compartida** que utilizan todos los contextos +(admin y public). Es la fundación del proyecto y no debe tener dependencias de frameworks, librerías externas, o +capas superiores. + +## Principios + +1. **Sin dependencias externas**: No depende de WordPress, plugins, o librerías +2. **Lógica pura**: Solo reglas de negocio y objetos de dominio +3. **Inmutabilidad**: Los Value Objects son inmutables +4. **Validación**: Los objetos se validan a sí mismos + +## Estructura + +``` +shared/Domain/ +├── ValueObjects/ # Value Objects compartidos (ComponentID, SettingValue, etc.) +├── Exceptions/ # Excepciones de dominio (InvalidComponentException, etc.) +└── Contracts/ # Interfaces de repositorios y servicios de dominio +``` + +## Ejemplos de Uso + +### Value Objects + +Value Objects que representan conceptos del dominio: + +```php +namespace ROITheme\Shared\Domain\ValueObjects; + +final class ComponentID +{ + private int $value; + + public function __construct(int $value) + { + if ($value <= 0) { + throw new \InvalidArgumentException('Component ID must be positive'); + } + + $this->value = $value; + } + + public function value(): int + { + return $this->value; + } + + public function equals(ComponentID $other): bool + { + return $this->value === $other->value; + } +} +``` + +### Excepciones + +Excepciones específicas del dominio: + +```php +namespace ROITheme\Shared\Domain\Exceptions; + +class InvalidComponentException extends \DomainException +{ + public static function withId(int $id): self + { + return new self("Component with ID {$id} is invalid"); + } +} +``` + +### Contracts (Interfaces) + +Interfaces que definen comportamientos: + +```php +namespace ROITheme\Shared\Domain\Contracts; + +interface ComponentRepositoryInterface +{ + public function findById(ComponentID $id): ?Component; + public function save(Component $component): void; +} +``` + +## Reglas de Dependencia + +✅ **PUEDE** depender de: +- Otros objetos dentro de `shared/Domain/` +- SPL (Standard PHP Library) +- Nada más + +❌ **NO PUEDE** depender de: +- `shared/Application/` +- `shared/Infrastructure/` +- `admin/` o `public/` +- WordPress functions +- Librerías externas + +## Testing + +Los objetos de esta capa se testean con **tests unitarios puros**, sin necesidad de WordPress: + +```php +// tests/Unit/Shared/Domain/ValueObjects/ComponentIDTest.php +public function test_creates_valid_component_id() +{ + $id = new ComponentID(123); + $this->assertEquals(123, $id->value()); +} +``` + +## Cuándo Agregar Código Aquí + +Agrega código a `shared/Domain/` cuando: +- Es lógica de negocio pura (sin WordPress) +- Es compartido por admin/ y public/ +- Es un concepto fundamental del dominio +- Necesita alta cohesión y bajo acoplamiento + +No agregues aquí: +- Código que depende de WordPress +- Lógica específica de un solo contexto +- Implementaciones concretas de servicios diff --git a/shared/Domain/ValueObjects/.gitkeep b/shared/Domain/ValueObjects/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/Infrastructure/README.md b/shared/Infrastructure/README.md new file mode 100644 index 00000000..d1d6832b --- /dev/null +++ b/shared/Infrastructure/README.md @@ -0,0 +1,217 @@ +# Capa de Infraestructura - Shared (Implementaciones Concretas) + +## Propósito + +La capa de Infraestructura de `shared/` contiene **implementaciones concretas** de los servicios definidos en +Application, utilizando WordPress y otras librerías externas. + +## Principios + +1. **Implementaciones concretas**: Código que interactúa con WordPress y librerías +2. **Cumple contratos**: Implementa las interfaces definidas en Application +3. **Detalles técnicos**: Maneja aspectos técnicos como caché, base de datos, APIs +4. **Reusabilidad**: Servicios compartidos por todos los contextos + +## Estructura + +``` +shared/Infrastructure/ +├── Services/ # Implementaciones de servicios (WordPressCacheService, etc.) +└── Traits/ # Traits reutilizables (WordPress helpers, etc.) +``` + +## Ejemplos de Uso + +### Services (Implementaciones Concretas) + +Implementación de CacheService usando WordPress Transients: + +```php +namespace ROITheme\Shared\Infrastructure\Services; + +use ROITheme\Shared\Application\Contracts\CacheServiceInterface; + +final class WordPressCacheService implements CacheServiceInterface +{ + public function get(string $key): mixed + { + return get_transient($this->prefixKey($key)); + } + + public function set(string $key, mixed $value, int $ttl = 3600): bool + { + return set_transient($this->prefixKey($key), $value, $ttl); + } + + public function delete(string $key): bool + { + return delete_transient($this->prefixKey($key)); + } + + public function flush(): bool + { + return wp_cache_flush(); + } + + private function prefixKey(string $key): string + { + return 'roi_theme_' . $key; + } +} +``` + +Implementación de ValidationService usando WordPress Sanitization: + +```php +namespace ROITheme\Shared\Infrastructure\Services; + +use ROITheme\Shared\Application\Services\BaseValidationService; + +final class WordPressValidationService extends BaseValidationService +{ + public function validate(array $data, array $rules): array + { + $validated = []; + + foreach ($rules as $field => $rule) { + if (!isset($data[$field])) { + $this->errors[$field] = "Field {$field} is required"; + continue; + } + + $validated[$field] = match($rule) { + 'email' => sanitize_email($data[$field]), + 'text' => sanitize_text_field($data[$field]), + 'int' => intval($data[$field]), + 'url' => esc_url_raw($data[$field]), + default => $data[$field] + }; + } + + return $validated; + } +} +``` + +### Traits (Helpers Reutilizables) + +Traits para funcionalidad común de WordPress: + +```php +namespace ROITheme\Shared\Infrastructure\Traits; + +trait WordPressMetaDataTrait +{ + private function getMeta(int $postId, string $key, bool $single = true): mixed + { + return get_post_meta($postId, $this->prefixMetaKey($key), $single); + } + + private function updateMeta(int $postId, string $key, mixed $value): bool + { + return update_post_meta($postId, $this->prefixMetaKey($key), $value); + } + + private function deleteMeta(int $postId, string $key): bool + { + return delete_post_meta($postId, $this->prefixMetaKey($key)); + } + + private function prefixMetaKey(string $key): string + { + return '_roi_' . $key; + } +} +``` + +## Relación con Application + +La capa Infrastructure **implementa** los contratos definidos en Application: + +``` +shared/Application/Contracts/CacheServiceInterface.php (interface) + ↓ implementa +shared/Infrastructure/Services/WordPressCacheService.php (implementación) +``` + +## Inyección de Dependencias + +Los servicios se registran en el DI Container y se inyectan a través de constructores: + +```php +// En el DI Container (configuración) +$container->bind( + CacheServiceInterface::class, + WordPressCacheService::class +); + +// En el código que usa el servicio +public function __construct( + private readonly CacheServiceInterface $cache +) {} +``` + +## Reglas de Dependencia + +✅ **PUEDE** depender de: +- `shared/Domain/` (Value Objects, Entities) +- `shared/Application/` (especialmente Contracts) +- WordPress functions +- Librerías externas (Composer packages) + +❌ **NO PUEDE** depender de: +- `admin/` o `public/` (contextos específicos) + +## Testing + +Los servicios de Infrastructure se testean con **tests de integración** que usan WordPress: + +```php +// tests/Integration/Shared/Infrastructure/Services/WordPressCacheServiceTest.php +public function test_stores_and_retrieves_from_cache() +{ + $cache = new WordPressCacheService(); + + $cache->set('test_key', 'test_value', 3600); + $result = $cache->get('test_key'); + + $this->assertEquals('test_value', $result); +} +``` + +## Cuándo Agregar Código Aquí + +Agrega código a `shared/Infrastructure/` cuando: +- Implementas un servicio definido en Application/Contracts/ +- Necesitas interactuar con WordPress (base de datos, caché, opciones, etc.) +- Integras una librería externa +- Creas utilidades de bajo nivel compartidas + +No agregues aquí: +- Lógica de negocio (va en Domain/) +- Definiciones de interfaces (van en Application/Contracts/) +- Código específico de admin o public +- Código sin contrato (sin interface) + +## Servicios Comunes en Infrastructure + +### Services/ +- `WordPressCacheService`: Caché usando WordPress Transients +- `WordPressValidationService`: Validación con WordPress sanitization +- `WordPressLoggerService`: Logging usando error_log o debug.log +- `WordPressDatabaseService`: Interacciones directas con $wpdb + +### Traits/ +- `WordPressMetaDataTrait`: Helpers para post meta +- `WordPressTaxonomyTrait`: Helpers para términos y taxonomías +- `WordPressHooksTrait`: Helpers para actions y filters +- `WordPressSanitizationTrait`: Helpers para sanitización + +## Patrón de Diseño + +Seguimos el patrón **Adapter**: +- WordPress es el "adaptee" (sistema externo) +- Nuestros Services son "adapters" +- Los Contracts son las interfaces que queremos + +Esto permite cambiar WordPress por otro sistema en el futuro sin afectar el código de negocio. diff --git a/shared/Infrastructure/Services/.gitkeep b/shared/Infrastructure/Services/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/shared/Infrastructure/Traits/.gitkeep b/shared/Infrastructure/Traits/.gitkeep new file mode 100644 index 00000000..e69de29b