Domain-Driven Design in PHP

#[Aggregate], #[AggregateIdentifier], aggregate command handlers, repository abstraction, #[Saga] as process managers, bounded-context isolation via Distributed Bus, Domain Events as first-class plain PHP classes.

Composer package · Laravel, Symfony, or Tempest · PostgreSQL or MySQL · RabbitMQ, Kafka, SQS, Redis, or DBAL outbox

How it compares

What Ecotone adds over a single-purpose tool

Each tool below is a capable choice for its slice. Ecotone covers the same ground and adds the operational layer around it — so aggregates, projections, sagas, and messaging share one model and one set of guarantees.

Spatie laravel-event-sourcing

Ergonomic event sourcing for Laravel domains.

Where Ecotone goes further than this library

  • Ecotone expresses the full tactical-DDD vocabulary as attributes — #[Aggregate], #[EventSourcingAggregate], command handlers on aggregate methods — on Laravel or Symfony.
  • #[Saga] adds an identifier-mapped process manager that lives across events arriving over time.
  • Repository abstraction over DBAL, Eloquent, or your own implementation.
  • Bounded-context isolation via the Distributed Bus + Service Map.
  • CQRS buses, the outbox, and async messaging come in the same model rather than as separate libraries.
EventSauce

Clean DDD ergonomics with trait-based aggregates.

Where Ecotone goes further than this library

  • Ecotone wraps the same aggregate and event-store ergonomics in CQRS buses, sagas, and distribution — one model, no separate dispatcher to wire.
  • #[Saga] as a first-class identifier-mapped process manager.
  • Bounded-context distribution via the Distributed Bus + Service Map.
  • Shared middleware — retry, DLQ, idempotency, PII, OpenTelemetry — applies across every building block uniformly.
Patchlevel event-sourcing

Strong attribute-based aggregate modelling.

Where Ecotone goes further than this library

  • Ecotone shares the same attribute-driven modelling and adds the surrounding architecture: CQRS buses, sagas, orchestration, and cross-service distribution.
  • #[Saga] as a first-class identifier-mapped process manager across events arriving over time.
  • Bounded-context distribution via the Distributed Bus + Service Map.
  • Runs on Laravel, Symfony, Tempest, or Ecotone Lite, with shared retry / DLQ / idempotency / PII middleware across every building block.
Ecotone

DDD vocabulary as PHP attributes — no base classes, no DSL between domain and framework.

  • Aggregates as plain final classes with #[Aggregate].
  • Event-sourced aggregates with #[EventSourcingAggregate] and #[EventSourcingHandler] event-application methods.
  • Sagas as process managers via #[Saga] with #[Identifier] mapping events to instances by payload field, header, or expression.
  • Bounded contexts isolated operationally by the Distributed Bus + Service Map — strategic DDD made operational, not aspirational.
  • Domain Events as first-class plain PHP classes — no recordThat() mixin, no DomainEventInterface to implement, no library-specific event dispatcher to wire.
  • Repository abstraction via the inbuilt DBAL or Eloquent repository, or your own implementation.
  • The four tactical-DDD building blocks (aggregates, sagas, projections, domain events) live on one declarative model — same EcotoneLite testing harness across all four, same retry / dedup / PII / observability middleware applied uniformly, no inter-library wiring to reconcile.

Frequently asked questions

Haven’t found what you’re looking for? Contact us

Ecotone's vocabulary IS DDD vocabulary — #[Aggregate] for state-stored aggregates, #[EventSourcingAggregate] for event-sourced ones, aggregate command handlers via #[CommandHandler] directly on aggregate methods, repository abstraction via DBAL or Eloquent or your own implementation, #[Saga] as process managers with #[Identifier] mapping events to instances, bounded-context isolation via the Distributed Bus + Service Map, Domain Events as first-class plain PHP classes (no marker interface). Assemble the same tactical patterns from separate libraries and you own the glue between them — the repositories, the event store, the saga state, the bus wiring. Ecotone delivers them as one actively-maintained model on Laravel, Symfony, Tempest, and Ecotone Lite.
Plain final classes. #[Aggregate] marks the type, #[Identifier] declares the identity, command handlers live on the aggregate (a static factory for creation, an instance method for state-changing operations). No base class, no interface, no recordThat() mixin required. Domain events are plain PHP classes the aggregate records; Ecotone publishes them onto the event bus when the aggregate is persisted.
The Distributed Bus and Service Map move commands and events between PHP services over the brokers you operate. Each bounded context runs as its own deployable with its own database, communicating with other contexts via well-defined domain events on the bus. The translation between contexts becomes explicit: events emitted by Context A are subscribed to by translators in Context B using #[Distributed] handlers on each side. Strategic DDD made operational.
A #[Saga] is a process manager in Vernon's sense — it remembers state across events arriving over time, decides what to do next, and dispatches commands to keep the process moving. State persisted per #[Identifier]; on each event arrival, Ecotone reloads the saga from the database. Event-to-saga binding via payload field, header, or expression (identifierMapping). #[Delayed] handles saga timeouts without cron.

Be part of the change with EcotoneCurve

Unleash the power of Messaging in PHP
and push productivity to the higher level

Get started
Gradient
Shapes 1
Shapes 2
DiscordTwitterSupport and ContactTelegram