Symfony Bundle · PHP 8.2+ · Packagist
Un motor de integración para Symfony que centraliza tus APIs externas bajo contratos claros.
¿Por qué no HttpClient?
Usa HttpClient cuando necesitas una o dos llamadas simples. Usa IntegrationEngine cuando la API forma parte de tu arquitectura y quieres contratos claros, respuestas tipadas y una Anti-Corruption Layer entre el proveedor y tu dominio.
El Problema
Diferentes formatos, autenticaciones inconsistentes, lógica de cache duplicada. El código se fragmenta y cada nueva API es empezar de cero.
Tokens y cache reimplementados en cada integración.
Cada cliente HTTP tiene su propia estructura.
HTTP acoplado al dominio. Imposible aislar.
Cada desarrollador resuelve el problema a su manera.
Sin trazabilidad, sin logs unificados, sin contexto compartido.
Cómo Funciona
Un único punto de entrada. Cada paso tiene una responsabilidad clara.
OAuth, sesiones, API keys. El engine los resuelve y cachea automáticamente.
/orders/{id} se resuelve en tiempo de llamada. Fallo explícito si falta parámetro.
YAML → auth → capa de llamada. Cada capa sobreescribe a la anterior. Sin magia.
Cada acción define su propio Response DTO con contrato garantizado.
Client, cache y config source sustituibles con una línea en YAML.
make:integration genera Mapper, Response DTO y YAML en segundos.
Un comando lo genera todo
El comando hace las preguntas. Tú solo escribes la lógica.
$ php bin/console make:integration Github GetUser
El Call Site
Sin strings mágicos — todo a través de contratos.
// Action path: GET /orders $this->engine->send( GetOrdersAction::getName() ); // → GET /orders
// Action path: GET /orders/{id} $response = $this->engine->send( actionName: GetOrderAction::getName(), context: DefaultActionContext::create(['id' => $id]), ); // → GET /orders/42 \assert($response instanceof GetOrderResponse); // GetOrderResponse { id: 42, reference: 'ORD-001', items: [...] }
// Action path: POST /orders $response = $this->engine->send( actionName: CreateOrderAction::getName(), body: CreateOrderBody::create(['reference' => 'ORD-001']), headers: new CorrelationHeaders($correlationId), ); // → POST /orders { "reference": "ORD-001" } \assert($response instanceof CreateOrderResponse); // CreateOrderResponse { id: 99, reference: 'ORD-001', status: 'pending' }
// Action endpoint: POST /graphql $response = $this->engine->send( actionName: GetOrderAction::getName(), body: GetOrderBody::create(['id' => $id]), ); // → POST /graphql { "query": "...", "variables": { "id": 42 } } \assert($response instanceof GetOrderResponse); // GetOrderResponse { id: 42, reference: 'ORD-001', items: [...] }
Para el flujo completo (facade → service → domain) → README →
Diseño en Capas
Tres niveles que emergen solos. Usa los que necesites.
| Clase | Responsabilidad | Alcance |
|---|---|---|
| CreateChargeAction | Solo declara el método, el path y el DTO de respuesta. Sin lógica HTTP. | Acción concreta |
| GithubAction | Auth, path base y headers comunes de GitHub. Reutilizado por todas sus acciones. | Integración |
| AbstractAction | Contrato base que provee el engine. Extensible sin tocar el core. | Bundle |
El comando make:integration crea el config, las clases y el YAML en un solo paso.
Sin boilerplate. Sin decisiones arbitrarias. Solo tu lógica de negocio.