Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ddd Base Laravel Package

aulasoftwarelibre/ddd-base

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require aulasoftwarelibre/ddd-base
    

    Ensure your project meets the PHP version (^7.2) and extension (ext-mbstring) requirements.

  2. 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; }
    }
    
  3. Key Starting Points

    • Entities: src/AulaSoftwareLibre/DDDBase/Entity/Entity.php
    • Value Objects: src/AulaSoftwareLibre/DDDBase/ValueObject/ValueObject.php
    • Domain Events: src/AulaSoftwareLibre/DDDBase/Event/DomainEvent.php
    • Repositories: src/AulaSoftwareLibre/DDDBase/Repository/RepositoryInterface.php

Implementation Patterns

Core Workflows

1. Domain Modeling

  • Entities: Use the Entity 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));
        }
    }
    
  • Value Objects: Immutable objects for domain logic.
    class Money extends ValueObject
    {
        private float $amount;
        private string $currency;
    
        public function __construct(float $amount, string $currency)
        {
            $this->amount = $amount;
            $this->currency = $currency;
        }
    }
    

2. Event Sourcing

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
        ));
    }
}

3. Repository Integration

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();
    }
}

4. Domain Events

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()));
    }
}

Integration Tips

  • Doctrine ORM: Use doctrine/orm for persistence. Configure lifecycle callbacks for event publishing.
  • Symfony Messenger: Integrate with symfony/messenger for async event handling.
  • Prooph Event Store: Configure the PDO event store for event sourcing:
    # config/packages/prooph_event_store.yaml
    prooph_event_store:
        stores:
            default:
                event_store: prooph_event_store.pdo_event_store
    

Gotchas and Tips

Pitfalls

  1. Prooph Version Conflicts

    • The package requires specific Prooph versions (^4.2, ^5.6, etc.). Avoid upgrading Prooph packages without testing.
    • Fix: Pin versions in composer.json:
      "prooph/common": "4.2.0",
      "prooph/event-sourcing": "5.6.0"
      
  2. Entity Identity

    • The Entity base class assumes a UUID for identity by default. Override getId() if using a different strategy.
    • Tip: Use Ramsey\Uuid\Uuid for UUID generation:
      $this->id = Uuid::uuid4()->toString();
      
  3. Event Sourcing Snapshots

    • Prooph’s snapshotter requires proper configuration. Snapshots may not trigger if the Snapshotter is misconfigured.
    • Debug: Check prooph/snapshotter logs for snapshot failures.
  4. Circular Dependencies

    • Avoid circular references between aggregates. Use AggregateRoot carefully to prevent infinite loops.

Debugging

  • Event Publishing: Verify events are published by listening to the DomainEvent bus:
    $bus->subscribe(new class implements MessageHandlerInterface {
        public function __invoke(object $event): void
        {
            error_log("Event published: " . get_class($event));
        }
    });
    
  • Repository Queries: Use Doctrine’s query logging for complex repository methods:
    $this->em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
    

Extension Points

  1. 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");
            }
        }
    }
    
  2. 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
        }
    }
    
  3. 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);
        }
    }
    

Configuration Quirks

  • Doctrine Lifecycle Callbacks Ensure postPersist/postUpdate callbacks are registered for event publishing:
    /** @ORM\PostPersist */
    public function postPersist(): void
    {
        $this->publishDomainEvent(new UserCreated($this->id, $this->email));
    }
    
  • Prooph Event Store Configure the PDO event store connection in config/packages/prooph_event_store.yaml:
    prooph_event_store:
        stores:
            default:
                event_store: prooph_event_store.pdo_event_store
                connection: default
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor