# Ecotone Framework - Complete Reference > Ecotone is a PHP framework for building resilient, scalable, and business-oriented systems using message-driven architecture with DDD, CQRS, and Event Sourcing patterns. Ecotone works with Symfony, Laravel, or standalone using Ecotone Lite. It provides declarative configuration using PHP attributes, requiring no framework-specific class extensions. ## Installation ### Symfony ```bash composer require ecotone/symfony-bundle ``` ### Laravel ```bash composer require ecotone/laravel ``` ### Standalone (Ecotone Lite) ```bash composer require ecotone/lite-application ``` ## Core Concepts ### Command Handler Command handlers process commands that change system state: ```php use Ecotone\Modelling\Attribute\CommandHandler; class UserService { #[CommandHandler] public function register(RegisterUser $command): void { // Handle user registration } } ``` ### Event Handler Event handlers react to domain events: ```php use Ecotone\Modelling\Attribute\EventHandler; class NotificationService { #[EventHandler] public function onUserRegistered(UserWasRegistered $event): void { // Send welcome email } } ``` ### Query Handler Query handlers return data without side effects: ```php use Ecotone\Modelling\Attribute\QueryHandler; class UserQueryService { #[QueryHandler] public function getUser(GetUserById $query): User { return $this->repository->find($query->userId); } } ``` ### Asynchronous Processing Mark handlers as async using the #[Asynchronous] attribute: ```php use Ecotone\Modelling\Attribute\CommandHandler; use Ecotone\Messaging\Attribute\Asynchronous; class EmailService { #[Asynchronous('async_channel')] #[CommandHandler] public function sendEmail(SendEmail $command): void { // This runs asynchronously } } ``` ### State-Stored Aggregate Aggregates encapsulate business logic and state: ```php use Ecotone\Modelling\Attribute\Aggregate; use Ecotone\Modelling\Attribute\Identifier; use Ecotone\Modelling\Attribute\CommandHandler; #[Aggregate] class Order { #[Identifier] private string $orderId; private string $status; #[CommandHandler] public static function place(PlaceOrder $command): self { $order = new self(); $order->orderId = $command->orderId; $order->status = 'placed'; return $order; } #[CommandHandler] public function ship(ShipOrder $command): void { $this->status = 'shipped'; } } ``` ### Event-Sourced Aggregate Event sourcing stores all changes as events: ```php use Ecotone\Modelling\Attribute\Aggregate; use Ecotone\Modelling\Attribute\Identifier; use Ecotone\Modelling\Attribute\CommandHandler; use Ecotone\Modelling\WithEvents; #[Aggregate] class Wallet { use WithEvents; #[Identifier] private string $walletId; private int $balance = 0; #[CommandHandler] public static function create(CreateWallet $command): self { $wallet = new self(); $wallet->walletId = $command->walletId; $wallet->recordThat(new WalletCreated($command->walletId)); return $wallet; } #[CommandHandler] public function deposit(DepositMoney $command): void { $this->recordThat(new MoneyDeposited($command->amount)); } public function applyWalletCreated(WalletCreated $event): void { $this->walletId = $event->walletId; } public function applyMoneyDeposited(MoneyDeposited $event): void { $this->balance += $event->amount; } } ``` ### Saga / Process Manager Sagas coordinate long-running processes across aggregates: ```php use Ecotone\Modelling\Attribute\Saga; use Ecotone\Modelling\Attribute\Identifier; use Ecotone\Modelling\Attribute\EventHandler; #[Saga] class OrderFulfillmentSaga { #[Identifier] private string $orderId; #[EventHandler] public static function start(OrderPlaced $event): self { $saga = new self(); $saga->orderId = $event->orderId; return $saga; } #[EventHandler] public function onPaymentReceived(PaymentReceived $event): void { // Trigger shipping } } ``` ## Testing with Ecotone Lite Ecotone Lite provides powerful testing capabilities: ```php use Ecotone\Lite\EcotoneLite; use Ecotone\Lite\Test\FlowTestSupport; class OrderTest extends TestCase { public function test_order_can_be_placed(): void { $ecotone = EcotoneLite::bootstrapFlowTesting( classesToResolve: [Order::class], containerOrAvailableServices: [new Order()] ); $ecotone ->sendCommand(new PlaceOrder('order-123')) ->sendCommand(new ShipOrder('order-123')); $order = $ecotone->getAggregate(Order::class, 'order-123'); $this->assertEquals('shipped', $order->getStatus()); } } ``` ## Message Channels Configuration ### RabbitMQ ```php use Ecotone\Amqp\AmqpBackedMessageChannelBuilder; class MessagingConfiguration { #[ServiceContext] public function rabbitChannel(): AmqpBackedMessageChannelBuilder { return AmqpBackedMessageChannelBuilder::create('async_channel'); } } ``` ### Database (DBAL) ```php use Ecotone\Dbal\DbalBackedMessageChannelBuilder; class MessagingConfiguration { #[ServiceContext] public function databaseChannel(): DbalBackedMessageChannelBuilder { return DbalBackedMessageChannelBuilder::create('async_channel'); } } ``` ## Error Handling and Resilience ### Automatic Retries with Dead Letter ```php use Ecotone\Messaging\Attribute\ServiceContext; use Ecotone\Messaging\Handler\Recoverability\ErrorHandlerConfiguration; use Ecotone\Messaging\Handler\Recoverability\RetryTemplateBuilder; class EcotoneConfiguration { #[ServiceContext] public function errorHandling(): ErrorHandlerConfiguration { return ErrorHandlerConfiguration::createWithDeadLetterChannel( 'errorChannel', RetryTemplateBuilder::exponentialBackoff(1000, 10) ->maxRetryAttempts(3), 'dbal_dead_letter' // Messages go here after retries exhausted ); } } ``` ### Dead Letter Queue Failed messages after all retries are moved to a dead letter queue for manual inspection. ### Outbox Pattern with Combined Message Channels Use database for reliable message storage (outbox), then forward to external broker for scaling: ```php use Ecotone\Amqp\AmqpBackedMessageChannelBuilder; use Ecotone\Dbal\DbalBackedMessageChannelBuilder; use Ecotone\Messaging\Attribute\ServiceContext; use Ecotone\Messaging\Channel\CombinedMessageChannel; class MessagingConfiguration { #[ServiceContext] public function messageChannels(): array { return [ // Database channel for outbox (transactional safety) DbalBackedMessageChannelBuilder::create('database_channel'), // RabbitMQ channel for scaling consumers AmqpBackedMessageChannelBuilder::create('rabbit_channel'), ]; } #[ServiceContext] public function combinedChannel(): CombinedMessageChannel { // Reference name 'outbox_rabbit' combines both channels return CombinedMessageChannel::create( 'outbox_rabbit', ['database_channel', 'rabbit_channel'] ); } } ``` Then use combined channel in handlers: ```php #[Asynchronous('outbox_rabbit')] #[EventHandler(endpointId: 'orderNotification')] public function handle(OrderWasPlaced $event): void { // Message stored in DB first (outbox), then forwarded to RabbitMQ } ``` ## Distributed Systems ### Distributed Bus Send commands/events across microservices: ```php use Ecotone\Modelling\DistributedBus; class OrderService { public function __construct(private DistributedBus $distributedBus) {} public function placeOrder(PlaceOrder $command): void { $this->distributedBus->sendCommand( 'inventory-service', 'reserveStock', new ReserveStock($command->productId) ); } } ``` ## Key Documentation Links - Full Documentation: https://docs.ecotone.tech - Installation: https://docs.ecotone.tech/install-php-service-bus - Quick Start: https://docs.ecotone.tech/quick-start-php-ddd-cqrs-event-sourcing - Message Bus and CQRS: https://docs.ecotone.tech/modelling/command-handling - Async Processing: https://docs.ecotone.tech/modelling/asynchronous-handling - Event Sourcing: https://docs.ecotone.tech/modelling/event-sourcing - Business Workflows: https://docs.ecotone.tech/modelling/business-workflows - Testing: https://docs.ecotone.tech/modelling/testing-support - Recovering and Monitoring: https://docs.ecotone.tech/modelling/recovering-tracing-and-monitoring - Symfony Integration: https://docs.ecotone.tech/modules/symfony/symfony-ddd-cqrs-event-sourcing - Laravel Integration: https://docs.ecotone.tech/modules/laravel/laravel-ddd-cqrs-event-sourcing ## Code Repository - GitHub: https://github.com/ecotoneframework/ecotone-dev - Packagist: https://packagist.org/packages/ecotone/ecotone ## AI Integration - MCP Server: https://docs.ecotone.tech/~gitbook/mcp - Install MCP in VSCode: vscode:mcp/install?%7B%22name%22%3A%22Ecotone%22%2C%22url%22%3A%22https%3A%2F%2Fdocs.ecotone.tech%2F~gitbook%2Fmcp%22%7D - Discord Community: https://discord.gg/GwM2BSuXeg ## Recommended Reading These articles explain Ecotone's approach and philosophy: - Implementing Event-Driven Architecture in PHP: https://blog.ecotone.tech/implementing-event-driven-architecture-in-php/ - Building Workflows in PHP: https://blog.ecotone.tech/building-workflows-in-php/ - Write Only Business Logic: Eliminate Boilerplate: https://blog.ecotone.tech/write-only-business-logic-eliminate-boilerplate/ - Building Blocks: Aggregates, Sagas, Event Sourcing: https://blog.ecotone.tech/building-blocks-exploring-aggregates-sagas-event-sourcing/ - Async Failure Recovery: Queue vs Streaming Strategies: https://blog.ecotone.tech/async-failure-recovery-queue-vs-streaming-channel-strategies/ - Building Workflows in PHP with Ecotone: https://blog.ecotone.tech/building-workflows-in-php-with-ecotone/ - Building Reactive Message-Driven Systems in PHP: https://blog.ecotone.tech/building-reactive-message-driven-systems-in-php - DDD and Messaging with Laravel and Ecotone: https://blog.ecotone.tech/ddd-and-messaging-with-laravel-and-ecotone - Laravel Multi-Tenant Systems with Ecotone: https://blog.ecotone.tech/laravel-multi-tenant-systems-with-ecotone - Symfony Multi-Tenant Applications with Ecotone: https://blog.ecotone.tech/symfony-multi-tenant-applications-with-ecotone - Building Resilient and Scalable Systems by Default: https://blog.ecotone.tech/building-resilient-and-scalable-systems-by-default - Message Channels: Zero Configuration Async Processing: https://blog.ecotone.tech/message-channels-zero-configuration-async-processing - All Blog Articles: https://blog.ecotone.tech