PHPackages                             becklyn/ddd-core - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. becklyn/ddd-core

ActiveLibrary[Framework](/categories/framework)

becklyn/ddd-core
================

DDD framework for PHP

4.0.0(3y ago)17.4k↓35%3MITPHPPHP &gt;=8.0

Since Jul 21Pushed 3y agoCompare

[ Source](https://github.com/Becklyn/ddd-core)[ Packagist](https://packagist.org/packages/becklyn/ddd-core)[ RSS](/packages/becklyn-ddd-core/feed)WikiDiscussions 4.x Synced 1mo ago

READMEChangelog (5)Dependencies (6)Versions (10)Used By (3)

becklyn/ddd-core is a set of components for developing software with domain-driven design, event sourcing and CQRS. Support is included for:

- entity identity
- domain events and their handling
- event store and event sourcing
- transaction handling
- command handling

The components are designed to facilitate the following workflow:

- A command is dispatched through a command bus
- This initiates a transaction where a command handler processes the command and performs the desired action on an aggregate or executes a domain service
- If an unhandled exception occurs, the transaction is rolled back and all changes are discarded
- Otherwise, the transaction is committed, events raised by the aggregate or domain service are collected, persisted to the event store, and dispatched through an event bus
- Event subscribers listen to the events and dispatch new commands if other aggregates need to be modified in response, or if other kinds of actions need to be undertaken
- This repeats the "core transaction loop" until all events are resolved and no new commands are initiated, or in other words until eventual consistency is achieved.

Usage
-----

[](#usage)

becklyn/ddd-core provides the components mostly as abstract classes and interfaces to facilitate use independent of any specific technological infrastructure. Our choice is Symfony with Doctrine and SimpleBus, and we provide implementations tying everything together in the becklyn/ddd-doctrine-bridge and becklyn/ddd-symfony-bridge libraries. If you wish to use other technologies, you will need to provide your own implementations for the event store and event, transaction and command handling.

### Core Loop

[](#core-loop)

Here's a more detailed explanation on how to achieve the becklyn/ddd-core workflow:

- Dispatch a command via the `CommandBus`. Commands are plain PHP classes of your own creation, essentially DTOs, and they should contain all the data necessary to perform the desired action. At a minimum, they should contain the identity of the aggregate being manipulated.
- The `CommandBus` should route the command to its corresponding handler. The implementation provided by becklyn/ddd-symfony-bridge does this automatically within a properly configured Symfony application. Otherwise, you will have to ensure this yourself.
- Process the command in a handler extending the abstract `CommandHandler` class:
    - Each command class must have exactly one handler class.
    - Your handler should have a public method accepting the command as its argument, and the method should call `CommandHandler::handleCommand`.
    - `handleCommand` will in turn call the abstract `execute` method in which you should implement your command handling logic:
        - Load an aggregate implementing the `EventProvider` interface (we recommend doing so from a repository).
        - Perform an action on the aggregate, raising domain events within it (you can use the `EventProviderCapabilities` or `EventSourcedProviderCapabilities` trait in the aggregate to facilitate this).
        - Return the aggregate fom the `execute` method.
    - `CommandHandler` will dequeue any events raised by the aggregate returned from `execute`, register them with the `EventRegistry` and commit the transaction through the `TransactionManager`.
    - Alternatively, a domain service may be called from `execute`. In such cases, the service should use the `EventRegistry` to dequeue and register any events raised by the affected aggregate, and `CommandHandler::execute` should return null.
- `TransactionManager::commit` should take care of any persistence concerns and call `EventManager::flush`. Similarly, `TransactionManager::rollback` should discard and changes and call `EventManager::clear`. A Doctrine implementation is provided by becklyn/ddd-doctrine-bridge.
- Flushing the `EventManager` collects all events registered by the `EventRegistry` and dispatches them through the `EventBus`. Clearing the `EventManager` simply discards all events.
- The `EventBus` should dispatch the events to any subscribers subscribing to them. becklyn/ddd-symfony-bridge provides a Symfony/SimpleBus implementation that does this automatically within a properly configured Symfony application.
- If any other aggregates need to react to changes made to the initial aggregate, subscribe to the relevant events through event subscribers:
    - Multiple subscribers may subscribe to any single event. It is not recommended that subscribers depend on the order in which they process events, nor that a subscriber should stop the propagation of an event. While we enforce these practices in becklyn/ddd-symfony-bridge, we do not impose any restrictions on your own implementations.
    - The subscribers shouldn't contain any domain logic, but should instead generally only dispatch new commands.

### Entities, Aggregates and Events

[](#entities-aggregates-and-events)

Entities must implement the `EventProvider` interface, and they must raise a domain event for every change to their state. The `EventProviderCapabilities` can be used by entities to facilitate this. Each entity must also have its corresponding identity class implementing the `EntityId` interface. `AbstractEntityId` is provided for a default implementation.

One entity in every aggregate serves as the aggregate root. Any and all interaction with the aggregate is allowed only through this entity, and thus only aggregate roots should have repositories. Aggregate root identities must implement the `AggregateId` interface instead of just `EntityId`. `AbstractAggregateId` is provided for a default implementation. When `dequeueEvents` is called on an aggregate root, it should in turn collect all of the events from other entities in the aggregate and return them along the events raised by the aggregate root.

Events must implement the `DomainEvent` interface and may do so extending the `AbstractDomainEvent` class. A domain event records a state change and must contain all the data necesary for it to be replayed from the previous state.

If using event sourcing, aggregates should use the `EventSourcedProviderCapabilities` trait instead of `EventProviderCapabilities`. While it is possible to implement repositories using the `EventStore` and its `getAggregateStream` method directly, this will likely result in low performance for most implementations if a large number of aggregates is fetched at once. For such scenarios we recommend using projections instead.

Testing
-------

[](#testing)

We write our PHPUnit/Prophecy unit tests inspired by the BDD workflow of "given/when/then". To test the code interacting with components from this library, we have gathered various given/when/then helper methods into traits. You can find them within the Testing namespaces of individual subdomans present in the library, for example:

- `Becklyn\Ddd\Commands\Testing\CommandHandlerTestTrait`
- `Becklyn\Ddd\Events\Testing\DomainEventTestTrait`

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity25

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 93.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~50 days

Recently: every ~0 days

Total

9

Last Release

1363d ago

Major Versions

1.x-dev → 3.0.02022-08-24

3.x-dev → 4.0.02022-08-25

PHP version history (2 changes)2.0.0PHP &gt;=8.0

1.0.0PHP &gt;=7.4

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1044355?v=4)[Becklyn Studios](/maintainers/becklyn)[@Becklyn](https://github.com/Becklyn)

---

Top Contributors

[![msvujnovic-becklyn](https://avatars.githubusercontent.com/u/74408165?v=4)](https://github.com/msvujnovic-becklyn "msvujnovic-becklyn (15 commits)")[![msvujnovic](https://avatars.githubusercontent.com/u/22997161?v=4)](https://github.com/msvujnovic "msvujnovic (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/becklyn-ddd-core/health.svg)

```
[![Health](https://phpackages.com/badges/becklyn-ddd-core/health.svg)](https://phpackages.com/packages/becklyn-ddd-core)
```

###  Alternatives

[laravel/framework

The Laravel Framework.

34.7k509.9M17.0k](/packages/laravel-framework)[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[laravel-zero/framework

The Laravel Zero Framework.

3371.4M369](/packages/laravel-zero-framework)[magento/community-edition

Magento 2 (Open Source)

12.1k52.1k10](/packages/magento-community-edition)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[laravel-lang/publisher

Localization publisher for your Laravel application

2167.7M24](/packages/laravel-lang-publisher)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
