Saltar a contenido

Mapa de dependencias — línea base

Generado a partir del código (bin/convoca-inventario.py) y verificado a mano en los puntos que se marcan como comprobado. Complementa al inventario de funcionalidades.

1. Capas reales

                    ┌──────────────────────────────┐
   presentación     │ theme (patrones, estilos)    │
                    │ shortcodes y bloques         │
                    └──────────────┬───────────────┘
                                   │ hooks y options
   dominio          ┌──────────────┴───────────────┐
                    │ members  enroll  shifts      │
                    │ courses  publisher  gateway  │
                    └──────────────┬───────────────┘
                                   │ \Convoca\Core\*
   infraestructura  ┌──────────────┴───────────────┐
                    │ core: Logger, Utils, Mailer, │
                    │ PDF, Upgrade, License,       │
                    │ Webhook, Hour_Ledger,        │
                    │ Module_Registry, Features    │
                    └──────────────────────────────┘

gateway es un caso híbrido: por debajo es infraestructura de cobro (lo usan enroll, members y courses), por arriba tiene su propio dominio (pagos, Redsys, recibos). Se queda como plugin propio; lo que no debe crecer es su superficie de infraestructura.

2. Dependencias declaradas (Requires Plugins)

Plugin Declara Comprobado
core — —
enroll convoca-core sí
members convoca-core sí
gateway convoca-core sí
shifts convoca-core sí
publisher convoca-core sí
courses convoca-core sí
license-generator convoca-core, woocommerce sí
assistant nada usa \Convoca\Core\Logger y \Convoca\Core\Utils
ext-gateway (demo) nada usa Convoca\Core, Gateway y Members; es una demo, no se distribuye
theme — depende de core vía hooks, sin cabecera

Hallazgo D1: convoca-assistant no declara Requires Plugins: convoca-core pero llama a \Convoca\Core\Utils::check_rate_limit() en REST_Controller sin guarda. Con Core desactivado, esa ruta REST produce error fatal. Arreglo de una línea, sin cambio de comportamiento.

3. Dependencias reales por código (quién usa qué de Core)

Plugin Clases de Convoca\Core que referencia
enroll Admin, Email_Copy, Email_Layout, Event_Style_Registry, Features, Hour_Ledger, License_Manager, Logger, Mailer, Member_Identity, Signature, Upgrade_Manager, Utils, Webhook_Manager
members Admin, Email_Copy, Email_Layout, Email_Links, Features, Installer, License_Manager, Logger, Notifications, Signature, Upgrade_Manager, Utils, Webhook_Manager
gateway Admin, License_Manager, Logger, Mailer, Payments, Upgrade_Manager, Utils
shifts Admin, Hour_Ledger, License_Manager, Logger, Mailer, Upgrade_Manager, Utils
courses Features, License_Manager, Logger, Mailer, Payments, Utils
publisher License_Manager, Mailer, Utils
assistant Logger, Utils
theme ninguna
health-check License_Manager, Logger, Migration_History, Module_Registry, Utils, Webhook_Manager

Logger, Utils y License_Manager son el contrato de facto de Core: los usan 9, 8 y 6 plugins respectivamente. Esas tres clases son API pública congelada: se pueden ampliar, no se pueden romper.

Referencias cruzadas entre plugins de dominio

Origen Destino Qué usa Guarda
enroll gateway Payment_Handler::create_payment() / build_payment_url() Features::is_gateway_active() (comprobado)
members gateway CPT_Pago, Payment_Handler, Receipt_Generator pendiente de comprobar caso a caso
courses gateway Receipt_Generator pendiente de comprobar
ext-gateway gateway + members Payment_Handler, CPT_Pago, CPT_Miembro demo
health-check enroll + gateway + members lectura para diagnóstico es su función

No hay dependencia circular de código entre enroll y members: lo que había era un class_exists comentado en Motor_Inscripcion. La comunicación va por hooks, que es lo correcto.

4. Contratos por hook

59 enlaces entre plugins, todos con prefijo convoca_. Los que importan:

Hook Emite Escuchan Crítico
convoca_gateway_payment_completed gateway (y ext-gateway) enroll, members, courses, core sí: cierra la inscripción y activa la cuota
convoca_gateway_payment_failed gateway enroll, members, courses sí
convoca_gateway_payment_refunded gateway courses sí
convoca_members_estado_changed members core (auditoría) sí
convoca_member_created members core no
convoca_voluntario_aprobado members shifts sí: habilita turnos
convoca_voluntario_revocado members shifts sí
convoca_enroll_inscripcion_nueva enroll core no
convoca_can_credit_hours core members sí: filtro de decisión
convoca_member_identity / _plans / _priority / _states core members sí: Core pregunta, Members responde
convoca_payment_backend / _reader / _receipt core gateway sí
convoca_register_module core ext-gateway extensión
convoca_license_create_api_token license-generator mu-plugins no

Patrón bueno ya establecido: Core pregunta por filtros, el plugin de dominio responde. Así Core no necesita conocer a Members ni a Gateway. Se debe conservar.

Patrón a vigilar: los do_action de pago los emite Gateway pero también los emite health-check para simular cobros. Es intencionado (diagnóstico), pero significa que un do_action de pago no prueba que haya habido un cobro real.

5. Acoplamiento oculto por opciones

9 opciones las lee más de un plugin sin pasar por un filtro:

Opción Dueño natural La leen
convoca_gateway_settings gateway gateway, members, core, health-check
convoca_members_settings members members, enroll, core
convoca_members_plans members members, core
convoca_pdf_templates core core, enroll, members
convoca_document_theme core core, members
convoca_shifts_hora_apertura / _cierre shifts shifts, core
convoca_publisher_auto_publish publisher publisher, health-check
convoca_uninstall_keep_data cada plugin 7 plugins (convención compartida, correcto)

Hallazgo D2: leer la opción de otro plugin funciona, pero convierte una decisión interna en contrato público sin documentarlo. No se debe romper (hay instalaciones leyéndola), pero toda lectura nueva debería ir por filtro. Documentar las 8 primeras como API de facto en la fase 6.

6. Librerías de terceros

Librería Dónde Copias Comentario
dompdf/dompdf ^3.0 core 1 correcto: infraestructura común
chillerlan/php-qrcode ^6.0 enroll, members, courses 3 cada plugin la vendoriza por separado
eluceo/ical ^2.9 enroll 1 dominio de enroll (calendario), correcto
stubs de PHPStan de Core members, shifts, courses, assistant 4 copias mantenidas a mano

Hallazgo D3: las cuatro copias de stubs de Core se desincronizan con cada cambio de API de Core. No afectan a producción (no se despliegan), pero cuestan mantenimiento y dan falsos verdes/falsos rojos en PHPStan. Candidato a script generador único.

Hallazgo D4: tres copias de chillerlan/php-qrcode. Moverla a Core ahorra ~3 copias en los paquetes, pero solo es seguro si los tres plugins declaran ya Requires Plugins: convoca-core (lo hacen). Requiere verificar el tamaño real de los paquetes antes de decidir: si el .distignore ya excluye vendor, el ahorro es cero y no se toca.

7. Regla de la casa (decidida por JC)

Capa Contenido No entra aquí
Core fechas, utilidades, PDF, logging, licencias, upgrades, webhooks, infraestructura común reglas de negocio de socios, inscripciones, pagos o turnos
Members socios, modalidades, cuotas, renovaciones, voluntariado generación de PDF genérica, transporte de correo
Enroll inscripciones, actividades, evaluaciones, check-in cobro, estados de socio
Gateway pagos, Redsys, recibos, conciliación creación de socios o de inscripciones
Shifts turnos, disponibilidad, horas de turno contabilidad de horas (vive en Core: Hour_Ledger)
Publisher publicación y distribución generación de carteles (vive en Enroll: media/)
Theme presentación, patrones, estilos lógica de negocio

Dos fronteras que hoy están borrosas y no se tocan sin plan de migración:

  1. Hour_Ledger está en Core pero su semántica es de dominio (horas de voluntariado). Se queda: lo usan members y shifts y moverlo rompe ambos.
  2. enroll/media/ y enroll/social/ (carteles, cuentas, cola social) son capacidades de publicación viviendo en Enroll. Moverlas a Publisher es un rediseño, no una refactorización: requiere migración de tablas y de opciones. No se ejecuta en esta fase.

8. Qué no se puede romper (contratos congelados)

  • Los 38 shortcodes y su salida pública.
  • Los 8 tipos de contenido y sus metadatos (_convoca_*).
  • Las 21 rutas REST y sus permisos.
  • Los 35 eventos cron y sus nombres (están agendados en instalaciones vivas).
  • Las 111 opciones existentes, incluidas las compartidas.
  • Los hooks de la tabla del punto 4, en nombre y firma de argumentos.
  • Las 8 tablas propias y su esquema actual (los cambios van con dbDelta aditivo).

Bibliotecas compartidas: una sola copia

chillerlan/php-qrcode (548 KB con su php-settings-container) la usaban tres plugins — Members (carné y certificado), Courses (certificado) y Enroll (QR de los correos)— y viajaba tres veces en cada sitio: 1.644 KB para el mismo código.

Desde Core 2.3.55 vive sólo en Core, que ya es el plugin del que dependen los demás y el que ya trae Dompdf. Los tres plugins la llaman igual (class_exists + new QRCode), pero quien la resuelve es el autoloader de Core, que se carga antes.

En los tres repos la dependencia queda en require-dev: así PHPStan y las pruebas la resuelven, y el empaquetado (composer install --no-dev) no la incluye. El paquete no lleva código que no se ejecuta.

Suelo de PHP: 8.2

La librería exige 8.2, y desde 2.3.55 Core lo declara. Los plugins que declaran Requires Plugins: convoca-core no pueden prometer menos: todo el ecosistema pasa a 8.2, salvo Enroll, que ya estaba en 8.4 por su librería de iCal. El tema se queda en 8.1 a propósito: su código corre ahí y degrada sin Core con function_exists.

Lo que costó y lo que ahorró: subir el suelo cierra la puerta a PHP 8.0/8.1 —que ningún sitio de Convoca usa— y ahorra 1,1 MB por instalación. Se decidió con el dato delante, no por gusto.