Skip to content

feat: unify all integrations on single Symfony-based container with dumped cache#675

Merged
dgafka merged 18 commits into
mainfrom
feat/container-support
Jun 12, 2026
Merged

feat: unify all integrations on single Symfony-based container with dumped cache#675
dgafka merged 18 commits into
mainfrom
feat/container-support

Conversation

@dgafka

@dgafka dgafka commented Jun 12, 2026

Copy link
Copy Markdown
Member

Why is this change proposed?

Ecotone maintained five separate container implementations — EcotoneLite's lazy in-memory container, the Symfony bundle's container merge, Laravel's per-definition singleton registration, Tempest's in-memory adapter, and LiteApplication's PHP-DI compilation. Each duplicated the same orchestration (cache handling, definition resolution, gateway exposure) with its own code and performance profile, and every new framework integration had to re-implement all of it.

This PR replaces all of them with one shared container system for Ecotone: a single Symfony-DI-based container in the main package that compiles once and dumps to a plain PHP file. Production boots require the dumped container — no annotation scanning, no definition building, no reflection — bringing Symfony-grade container caching to every integration. Because every framework now runs the same container, this opens the door for the next performance improvements to be delivered from a single place (dump format, lazy services, preloading) and benefit all integrations at once.

It also keeps user-land containers clean: Ecotone internals no longer leak into the application's container. Only the required services — Message Buses, Gateways, console commands, connection factories — are bridged into the framework container, while everything else stays inside Ecotone's own compiled container. The public surface stays 1:1: gateway/bus autowiring, console commands, and getGatewayByName() all work as before.

Description of Changes

  • New packages/Ecotone/src/SymfonyContainer/ module: converts Ecotone definitions to a Symfony ContainerBuilder, dumps via PhpDumper on cache-enabled boots, and wraps everything in a PSR-11 EcotoneContainer with lazy fallback to the framework container (two-way bridge: external references resolve lazily through a synthetic ecotone.external_container; gateways/buses are bridged back into the framework container).
  • EcotoneLite, Laravel, Symfony bundle, Tempest, and LiteApplication all boot through one shared EcotoneSymfonyContainerFactory::bootstrap() — cache hit loads the dumped container, cache miss lazily builds the messaging configuration. Old implementations (LazyInMemoryContainer, InMemoryContainerImplementation, SymfonyContainerAdapter merge, PHP-DI) are removed; php-di/php-di dependency dropped, symfony/dependency-injection added to ecotone/ecotone.
  • Integration surface minimized: shared ContainerCacheLayout (config-hash cache invalidation), typed accessors (getRegisteredConsoleCommands(), getDefinedServiceIds(), getConfigHash()), and registerBridgesInto() for gateway exposure — each framework integration is now just config mapping + a PSR adapter + bridge closures.
  • Warm boot path optimized: the dumped container loads through a tiny opcache-served loader stub (no file reads or parsing on the hot path), and the dumped container files are wired into Symfony's opcache preloading via the cache warmer — so preloaded production setups pay near-zero cost for Ecotone's container.

Usage examples

Nothing changes for end users — same APIs, now cache-backed everywhere:

// User application tests: after the first run, unchanged config boots from the
// dumped container in ~2ms — making test suites super efficient to run
$ecotone = EcotoneLite::bootstrapFlowTesting(
    [OrderService::class],
    [new OrderService()],
);

// Laravel/Symfony/Tempest: gateways keep resolving from the framework container
public function __construct(private CommandBus $commandBus) {}

Use cases

  1. Production: full HTTP requests through Ecotone run ~30% faster on Laravel and ~35% faster on cached EcotoneLite (dumped PHP container instead of unserializing definitions and rebuilding a runtime container), while Symfony stays at parity or better.
  2. Test suites in user applications: after the first run, EcotoneLite bootstraps with unchanged configuration drop to ~2ms per run (211ms cold → 2.1ms warm in our measurement) — tests become super efficient to run and execute.
  3. New framework integrations: implementing one means a config mapper, a PSR-11 adapter, and three-line bridge closures — the container, caching, and invalidation come from the shared module.
  4. Framework-wide optimizations: with one shared container system, future container improvements (dump format, lazy services, opcache preloading) land once in the shared module and apply to Lite, Laravel, Symfony, Tempest, and LiteApplication simultaneously.

Boot flow

flowchart TD
    A[Framework boot] --> B{Dumped container\nfor config hash?}
    B -- hit --> C[require opcache-served loader stub]
    B -- miss --> D[Scan annotations -> MessagingSystemConfiguration]
    D --> E[Convert definitions to Symfony ContainerBuilder]
    E --> F{Cache enabled?}
    F -- yes --> G[Compile + PhpDumper dump] --> C
    F -- no --> H[Use ContainerBuilder directly]
    C --> I[EcotoneContainer wrapper]
    H --> I
    I -- outbound --> J[Lazy delegate to framework container\nfor user services]
    I -- inbound --> K[Bridge Message Buses, Gateways, console commands\ninto framework container]
Loading

Benchmarks — full HTTP request through the whole application

Measured locally with phpbench on the example applications. This is the complete HTTP-stack benchmark: every request boots the entire application — framework kernel startup, container loading, Ecotone, and request handling — not just the Ecotone part. Run with opcache + opcode file cache enabled, mirroring how production amortizes compilation across processes.

Stack Before (merged/per-framework containers) After (shared dumped container)
Laravel prod 10.97ms 7.81ms (−29%)
Laravel dev 15.40ms 12.15ms (−21%)
Lite prod (cached) 8.71ms 5.65ms (−35%)
LiteApplication prod 2.31ms 1.58ms (−32%)
Symfony prod 2.53ms 2.49ms (−1.5%)
Symfony dev 3.07ms 2.99ms (−2.6%)
Lite test bootstrap in user app (cold → warm) 211ms 2.1ms (−99%)

All package suites green (Ecotone 1275, Laravel, Symfony, Tempest, LiteApplication, Dbal, EventSourcing, Kafka, Amqp, SQS, Redis + 87 cross-module tests).

Pull Request Contribution Terms

  • I have read and agree to the contribution terms outlined in CONTRIBUTING.

@dgafka dgafka force-pushed the feat/container-support branch from 73304ff to a20e01a Compare June 12, 2026 15:19
@dgafka dgafka merged commit 8b67402 into main Jun 12, 2026
10 of 12 checks passed
@dgafka dgafka deleted the feat/container-support branch June 12, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant