Installation
composer require aulasoftwarelibre/ddd-base
Ensure your project meets the PHP version (^7.2) and extension (ext-mbstring) requirements.
First Use Case: Domain Entities
Extend the provided Entity base class to define your domain entities:
use AulaSoftwareLibre\DDDBase\Entity\Entity;
class User extends Entity
{
protected string $name;
protected string $email;
public function __construct(string $name, string $email)
{
$this->name = $name;
$this->email = $email;
}
public function getName(): string { return $this->name; }
public function getEmail(): string { return $this->email; }
}
Key Starting Points
src/AulaSoftwareLibre/DDDBase/Entity/Entity.phpsrc/AulaSoftwareLibre/DDDBase/ValueObject/ValueObject.phpsrc/AulaSoftwareLibre/DDDBase/Event/DomainEvent.phpsrc/AulaSoftwareLibre/DDDBase/Repository/RepositoryInterface.phpEntity base class for aggregate roots with identity.
class Order extends Entity
{
private Collection $items;
private OrderStatus $status;
public function addItem(Product $product, int $quantity): void
{
$this->items->add(new OrderItem($product, $quantity));
}
}
class Money extends ValueObject
{
private float $amount;
private string $currency;
public function __construct(float $amount, string $currency)
{
$this->amount = $amount;
$this->currency = $currency;
}
}
Leveraging Prooph’s event-sourcing components:
use AulaSoftwareLibre\DDDBase\Event\DomainEvent;
use Prooph\EventSourcing\AggregateRoot;
class UserAggregate extends AggregateRoot
{
public function register(string $email, string $password): void
{
$this->recordThat(new UserRegistered(
$this->aggregateId(),
$email,
$password
));
}
}
Implement RepositoryInterface for persistence:
use AulaSoftwareLibre\DDDBase\Repository\RepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
class UserRepository implements RepositoryInterface
{
public function __construct(private EntityManagerInterface $em) {}
public function save(Entity $entity): void
{
$this->em->persist($entity);
$this->em->flush();
}
}
Publish events for asynchronous processing:
use AulaSoftwareLibre\DDDBase\Event\DomainEvent;
use Symfony\Component\Messenger\MessageBusInterface;
class UserRegisteredHandler
{
public function __construct(private MessageBusInterface $bus) {}
public function __invoke(UserRegistered $event): void
{
$this->bus->dispatch(new SendWelcomeEmail($event->email()));
}
}
doctrine/orm for persistence. Configure lifecycle callbacks for event publishing.symfony/messenger for async event handling.# config/packages/prooph_event_store.yaml
prooph_event_store:
stores:
default:
event_store: prooph_event_store.pdo_event_store
Prooph Version Conflicts
^4.2, ^5.6, etc.). Avoid upgrading Prooph packages without testing.composer.json:
"prooph/common": "4.2.0",
"prooph/event-sourcing": "5.6.0"
Entity Identity
Entity base class assumes a UUID for identity by default. Override getId() if using a different strategy.Ramsey\Uuid\Uuid for UUID generation:
$this->id = Uuid::uuid4()->toString();
Event Sourcing Snapshots
Snapshotter is misconfigured.prooph/snapshotter logs for snapshot failures.Circular Dependencies
AggregateRoot carefully to prevent infinite loops.DomainEvent bus:
$bus->subscribe(new class implements MessageHandlerInterface {
public function __invoke(object $event): void
{
error_log("Event published: " . get_class($event));
}
});
$this->em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
Custom Value Objects
Extend ValueObject to add validation or serialization logic:
class Email extends ValueObject
{
protected function validate(): void
{
if (!filter_var($this->value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException("Invalid email");
}
}
}
Domain Event Subscribers Create custom subscribers for domain events:
use AulaSoftwareLibre\DDDBase\Event\DomainEventSubscriber;
class UserEventSubscriber implements DomainEventSubscriber
{
public static function getSubscribedEvents(): array
{
return [
UserRegistered::class => 'onUserRegistered',
];
}
public function onUserRegistered(UserRegistered $event): void
{
// Handle event
}
}
Repository Decorators Decorate repositories for logging, caching, or additional logic:
class LoggingRepositoryDecorator implements RepositoryInterface
{
public function __construct(private RepositoryInterface $repository) {}
public function save(Entity $entity): void
{
error_log("Saving entity: " . get_class($entity));
$this->repository->save($entity);
}
}
postPersist/postUpdate callbacks are registered for event publishing:
/** @ORM\PostPersist */
public function postPersist(): void
{
$this->publishDomainEvent(new UserCreated($this->id, $this->email));
}
config/packages/prooph_event_store.yaml:
prooph_event_store:
stores:
default:
event_store: prooph_event_store.pdo_event_store
connection: default
How can I help you explore Laravel packages today?