arrow-left

All pages
gitbookPowered by GitBook
1 of 1

Loading...

About

Ecotone — The enterprise architecture layer for Laravel and Symfony

hashtag
Ecotone extends your existing Laravel and Symfony application with the enterprise architecture layer

One Composer package adds CQRS, Event Sourcing, Workflows, and production resilience to your codebase. No framework change. No base classes. Just PHP attributes on your existing code.

composer require ecotone/laravel    # or ecotone/symfony-bundle

hashtag
See what it looks like

That's the entire setup. No bus configuration. No handler registration. No retry config. No serialization wiring. Ecotone reads your attributes and handles the rest:

  • Command and Query Bus — wired automatically from your #[CommandHandler] and #[QueryHandler] attributes

  • Event routing — NotificationService subscribes to OrderWasPlaced without any manual wiring

hashtag
Test exactly the flow you care about

Extract a specific flow and test it in isolation — only the services you need:

Only OrderService is loaded. No notifications, no other handlers — just the flow you're verifying.

Now bring in the full async flow. Enable an in-memory channel and run it within the same test process:

->run('notifications') processes messages from the in-memory queue — right in the same process. The async handler executes deterministically, no timing issues, no polling, no external broker.

The key: swap the in-memory channel for , , or to test what runs in production — the test stays the same. Ecotone runs the consumer within the same process, so switching transports never changes how you test. The ease of in-memory testing no matter what backs your production system.


hashtag
What changes in your daily work

hashtag
Business logic is the only code you write

No command bus configuration. No handler registration. No message serialization setup. You write a PHP class with an attribute, and Ecotone wires the bus, the routing, the serialization, and the async transport. Your code stays focused on what your application actually does — your domain.

hashtag
Going async never means rewriting handlers

Add #[Asynchronous('channel')] to any handler. The handler code stays identical. Switch from synchronous to to to by changing one line of configuration. Your business logic never knows the difference.

hashtag
Failed messages don't disappear

Every failed message is captured in a . You see what failed, the full exception, and the original message. with one command. And can be combined with inbuilt Outbox pattern to ensure full consistency. No more silent failures. No more guessing what happened to that order at 3am.

hashtag
Complex workflows live in one place

A multi-step business process — order placement, payment, shipping, notification — doesn't need to be scattered across event listeners, cron jobs, and database flags. Ecotone gives you for stateful workflows, for linear pipelines, and for declarative process control. The entire business flow is readable in one class.

hashtag
Your codebase tells the story of your business

When a new developer opens your code, they see PlaceOrder, OrderWasPlaced, ShipOrder — not AbstractMessageBusHandlerFactory. Ecotone keeps your domain clean: no base classes to extend, no framework interfaces to implement, no infrastructure leaking into your business logic. Just with attributes that declare their intent.


hashtag
AI-ready by design

Ecotone's declarative, attribute-based architecture is inherently friendly to AI code generators. When your AI assistant works with Ecotone code, two things happen:

Less context needed, less code generated. A command handler with #[CommandHandler] and #[Asynchronous('orders')] tells the full story in two attributes — no bus configuration files, no handler registration, no retry setup to feed into the AI's context window. The input is smaller because there's less infrastructure to read, and the output is smaller because there's less boilerplate to generate. That means lower token cost, faster iteration cycles, and more accurate results.

AI that knows Ecotone. Your AI assistant can work with Ecotone out of the box:

  • — Ready-to-use skills that teach any coding agent how to correctly write handlers, aggregates, sagas, projections, tests, and more. Install with one command and your AI generates idiomatic Ecotone code from the start.

  • — Direct access to Ecotone documentation for any AI assistant that supports Model Context Protocol — Claude Code, Cursor, Windsurf, GitHub Copilot, and others.

  • — AI-optimized documentation files that give any LLM instant context about Ecotone's API and patterns.

Testing that AI can actually run. Ecotone's runs async flows in the same process — even complex workflows with sagas and projections can be tested with ->sendCommand() and ->run(). Your coding agent writes and verifies tests without needing to set up external infrastructure or guess at test utilities.

Declarative configuration that any coding agent can follow and reproduce. Testing support that lets it verify even the most advanced flows. Less guessing, no hallucinating — just confident iteration.


hashtag
The full capability set

Capability
What it gives you
Learn more

hashtag
The enterprise gap in PHP, closed

Every mature ecosystem has an enterprise architecture layer on top of its web framework:

Ecosystem
Web Framework
Enterprise Architecture Layer

Ecotone is built on the same foundation — — that powers Spring Integration, NServiceBus, and Apache Camel. In active development since 2017 and used in production by teams running multi-tenant, event-sourced systems at scale, Ecotone brings the same patterns that run banking, logistics, and telecom systems in Java and .NET to PHP.

This isn't about PHP catching up. It's about your team using proven architecture patterns — with the development speed that PHP gives you — without giving up the ecosystem you already know.


hashtag
Start with your framework

Laravel — Laravel's queue runs jobs, not business processes. Stop stitching Spatie + Laravel Workflow + Bus::chain + DIY outbox. Ecotone replaces the patchwork with one attribute-driven toolkit: aggregates with auto-published events, piped workflows, sagas, snapshots, transactional outbox — testable in-process, running on the queues you already have. composer require ecotone/laravel → ·

Symfony — Symfony Messenger handles dispatch. For aggregates, sagas, or event sourcing the usual path is bolting on a separate event sourcing library, rolling your own outbox, and writing dedup middleware per handler. Ecotone replaces the patchwork with one attribute-driven toolkit: aggregates, sagas, event sourcing, piped workflows, transactional outbox, and per-handler failure isolation so one failing listener doesn't double-charge customers on retry. Pure POPOs, Bundle auto-config, your Messenger transports preserved. composer require ecotone/symfony-bundle → ·

Any PHP framework — Ecotone Lite works with any PSR-11 compatible container. composer require ecotone/lite-application →


Try it in one handler. You don't need to migrate your application. Install Ecotone, add an attribute to one handler, and see what happens. If you like what you see, add more. If you don't — remove the package. Zero commitment.

  • — Setup guide for any framework

  • — Send your first command in 5 minutes

  • — Build a complete messaging flow step by step

circle-info

The full CQRS, Event Sourcing, and Workflow feature set is under the Apache 2.0 License. are available for teams that need advanced scaling, distributed bus with service map, orchestrators, and production-grade Kafka integration.

circle-check

Join — ask questions and share what you're building.

Async execution — #[Asynchronous('notifications')] routes to RabbitMQ, SQS, Kafka, or DBAL — your choice of transport
  • Failure isolation — each event handler gets its own copy of the message, so one handler's failure never blocks another

  • Retries and dead letter — failed messages retry automatically, permanently failed ones go to a dead letter queue you can inspect and replay

  • Tracing — OpenTelemetry integration traces every message across sync and async flows

  • Orchestrate multi-step business processes. Stateful workflows with compensation logic.

    Async Messaging

    RabbitMQ, Kafka, SQS, Redis, DBAL. One attribute to go async. Swap transports without code changes.

    Production Resilience

    Automatic retries, dead letter queues, outbox pattern, message deduplication, failure isolation.

    Domain-Driven Design

    Aggregates, domain events, bounded contexts. Pure PHP objects with no framework coupling.

    Distributed Bus

    Cross-service messaging. Share events and commands between microservices with guaranteed delivery.

    Multi-Tenancy

    Tenant-isolated processing, projections, and event streams. Built in, not bolted on.

    Observability

    OpenTelemetry integration. Trace every message — sync or async — across your entire system.

    Interceptors

    Cross-cutting concerns — authorization, logging, transactions — applied declaratively via attributes.

    Laravel / Symfony

    Ecotone

    Workshops, Support, Consultancy — Hands-on training for your team
    class OrderService
    {
        #[CommandHandler] 
        public function placeOrder(PlaceOrder $command, EventBus $eventBus): void
        {
            // your business logic
            $eventBus->publish(new OrderWasPlaced($command->orderId));
        }
    
        #[QueryHandler('order.getStatus')]
        public function getStatus(string $orderId): string
        {
            return $this->orders[$orderId]->status;
        }
    }
    
    class NotificationService
    {
        #[Asynchronous('notifications')]
        #[EventHandler]  
        public function whenOrderPlaced(OrderWasPlaced $event, NotificationSender $sender): void
        {
            $sender->sendOrderConfirmation($event->orderId);
        }
    }
    $ecotone = EcotoneLite::bootstrapFlowTesting([OrderService::class]);
    
    $ecotone->sendCommand(new PlaceOrder('order-1'));
    
    $this->assertEquals('placed', $ecotone->sendQueryWithRouting('order.getStatus', 'order-1'));
    $notifier = new InMemoryNotificationSender();
    
    $ecotone = EcotoneLite::bootstrapFlowTesting(
        [OrderService::class, NotificationService::class],
        [NotificationSender::class => $notifier],
        enableAsynchronousProcessing: [
            SimpleMessageChannelBuilder::createQueueChannel('notifications')
        ]
    );
    
    $ecotone
        ->sendCommand(new PlaceOrder('order-1'))
        ->run('notifications');
    
    $this->assertEquals(['order-1'], $notifier->getSentOrderConfirmations());

    CQRS

    Separate command and query handlers. Clean responsibility boundaries. Automatic bus wiring.

    Command Handling

    Event Sourcing

    Store events instead of state. Full audit trail. Rebuild read models anytime. Time travel and replay.

    Event Sourcing

    Java

    Spring Boot

    Spring Integration + Axon Framework

    .NET

    ASP.NET

    NServiceBus / MassTransit

    DBALarrow-up-right
    RabbitMQ
    Kafka
    stays with you
    RabbitMQ
    SQSarrow-up-right
    Kafka
    dead letter queue
    Replay it
    Sagas
    handler chaining
    Orchestrators
    plain PHP objects
    Agentic Skills
    MCP Server
    llms.txt
    testing support
    Enterprise Integration Patternsarrow-up-right
    Read more: Why Ecotone?
    Laravel Quick Start
    Laravel Module docs
    Symfony Quick Start
    Symfony Module docs
    Ecotone Lite docs
    Install
    Learn by example
    Go through tutorial
    free and open source
    Enterprise features
    Ecotone's Community Channelarrow-up-right

    Workflows & Sagas

    PHP

    Business Workflows
    Asynchronous Handling
    Resiliency
    Aggregates
    Microservices
    Multi-Tenancy
    Monitoring
    Interceptors