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 Laravel Package

pccomponentes/ddd

Mini framework PHP para construir aplicaciones con DDD + CQRS + Event Sourcing, orientado a la escritura. Propone arquitectura hexagonal (Application/Domain/Infrastructure/EntryPoint/Util) y guía de capas, dependencias y persistencia basada en eventos.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Steps to Begin
1. **Install the Package**
   Add the package via Composer in your Laravel project:
   ```bash
   composer require pccomponentes/ddd
  1. Understand the Hexagonal Structure Organize your project into the five layers:

    ├── Application
    ├── Domain
    ├── Infrastructure
    ├── EntryPoint
    └── Util
    
    • Domain: Contains entities, value objects, and domain services (pure business logic).
    • Application: Handles commands, command handlers, and use cases (orchestrates domain logic).
    • Infrastructure: Implements external dependencies (e.g., repositories for databases, message brokers).
    • EntryPoint: API controllers, console commands, or CLI interfaces (entry points for HTTP/CLI requests).
    • Util: Shared utilities (e.g., value objects, helpers).
  2. First Use Case: Create a Domain Entity Define a domain entity (e.g., User) in Domain/Entities/User.php:

    namespace Domain\Entities;
    
    use Domain\Model\AggregateRoot;
    use Domain\Model\Event\DomainEventInterface;
    
    class User extends AggregateRoot
    {
        public function register(string $name, string $email): void
        {
            $this->recordThat(new UserRegistered($name, $email));
        }
    }
    
  3. Define a Domain Event Create an event in Domain/Events/UserRegistered.php:

    namespace Domain\Events;
    
    use Domain\Model\Event\DomainEventInterface;
    
    class UserRegistered implements DomainEventInterface
    {
        public function __construct(
            public string $name,
            public string $email
        ) {}
    }
    
  4. Implement a Command Handler In Application/Commands/RegisterUserCommandHandler.php:

    namespace Application\Commands;
    
    use Domain\Entities\User;
    use Domain\Repositories\UserRepositoryInterface;
    
    class RegisterUserCommandHandler
    {
        public function __construct(
            private UserRepositoryInterface $userRepository
        ) {}
    
        public function handle(RegisterUserCommand $command): void
        {
            $user = User::create($command->name, $command->email);
            $user->register($command->name, $command->email);
            $this->userRepository->save($user);
        }
    }
    
  5. Set Up Infrastructure (Repository) Implement a repository in Infrastructure/Persistence/UserRepository.php:

    namespace Infrastructure\Persistence;
    
    use Domain\Entities\User;
    use Domain\Repositories\UserRepositoryInterface;
    
    class UserRepository implements UserRepositoryInterface
    {
        public function save(User $user): void
        {
            // Persist user and events using Event Sourcing (e.g., Doctrine, MongoDB ODM).
        }
    }
    
  6. Configure Dependency Injection Use Laravel’s service container to bind interfaces to implementations in EntryPoint/Http/Controllers/UserController.php:

    namespace EntryPoint\Http\Controllers;
    
    use Application\Commands\RegisterUserCommand;
    use Application\Commands\RegisterUserCommandHandler;
    use Illuminate\Http\Request;
    
    class UserController
    {
        public function __construct(
            private RegisterUserCommandHandler $handler
        ) {}
    
        public function register(Request $request)
        {
            $this->handler->handle(new RegisterUserCommand(
                $request->name,
                $request->email
            ));
        }
    }
    
  7. Leverage Value Objects Use built-in value objects (e.g., Uuid, DateTimeValueObject) in your domain:

    use Domain\Model\ValueObject\Uuid;
    
    $userId = Uuid::create(); // Generates UUIDv7 by default
    $userIdV4 = Uuid::v4();   // Explicitly generate UUIDv4
    

Implementation Patterns

Core Workflows

1. Event Sourcing Pattern

  • Domain Layer: Entities emit domain events (e.g., UserRegistered) when state changes occur.
  • Infrastructure Layer: Persist events to a store (e.g., MongoDB, PostgreSQL) and replay them to reconstruct entity state.
  • Example:
    // Domain/Entities/User.php
    class User extends AggregateRoot
    {
        public function changeEmail(string $newEmail): void
        {
            $this->recordThat(new EmailChanged($this->id, $newEmail));
        }
    }
    

2. CQRS Separation

  • Write Model: Focus on domain logic and event sourcing (handled by Application and Domain layers).
  • Read Model: Asynchronously project events into optimized read models (e.g., MongoDB, Elasticsearch) for queries.
  • Example:
    // Infrastructure/ReadModel/UserReadModel.php
    class UserReadModel
    {
        public function project(UserRegistered $event): void
        {
            UserRead::create([
                'id' => $event->userId,
                'name' => $event->name,
                'email' => $event->email,
            ]);
        }
    }
    

3. Command Handling

  • EntryPoint: Accepts HTTP/CLI requests and dispatches commands to handlers.
  • Application: Command handlers orchestrate domain logic.
  • Example:
    // EntryPoint/Http/Controllers/OrderController.php
    class OrderController
    {
        public function create(OrderRequest $request, CreateOrderCommandHandler $handler)
        {
            $handler->handle(new CreateOrderCommand(
                $request->userId,
                $request->productId,
                $request->quantity
            ));
        }
    }
    

4. Dependency Injection

  • Use Laravel’s container to inject dependencies (e.g., repositories, services) into command handlers or controllers.
  • Example:
    // config/container.php
    $container->bind(
        Domain\Repositories\UserRepositoryInterface::class,
        Infrastructure\Persistence\UserRepository::class
    );
    

Integration Tips

1. Event Sourcing with Laravel

  • Use Laravel’s events and listeners to trigger event persistence:
    // Domain/Entities/User.php
    protected function recordThat(DomainEventInterface $event): void
    {
        $this->events[] = $event;
        event(new DomainEventOccurred($event));
    }
    
  • Listen for DomainEventOccurred in Infrastructure to persist events:
    // Infrastructure/EventListeners/PersistDomainEvent.php
    class PersistDomainEvent
    {
        public function handle(DomainEventOccurred $event)
        {
            // Save event to event store (e.g., MongoDB, PostgreSQL).
        }
    }
    

2. Asynchronous Projections

  • Use Laravel’s queues to process events asynchronously:
    // Infrastructure/Jobs/ProjectUserReadModel.php
    class ProjectUserReadModel implements ShouldQueue
    {
        public function handle(UserRegistered $event)
        {
            UserRead::create([...]);
        }
    }
    
  • Dispatch the job in the event listener:
    ProjectUserReadModel::dispatch($event);
    

3. Value Objects in Forms/Requests

  • Validate and convert input to value objects in Laravel’s Form Requests:
    // EntryPoint/Http/Requests/RegisterUserRequest.php
    public function rules(): array
    {
        return [
            'email' => 'required|email',
        ];
    }
    
    public function prepareForValidation()
    {
        $this->merge([
            'user_id' => Uuid::create(), // Auto-generate UUIDv7
        ]);
    }
    

4. Testing Domain Logic

  • Test entities and value objects in isolation:
    // tests/Unit/Domain/UserTest.php
    public function test_user_registration()
    {
        $user = User::create('John Doe', 'john@example.com');
        $user->register('John Doe', 'john@example.com');
    
        $this->assertCount(1, $user->pullEvents());
        $this->assertInstanceOf(UserRegistered::class, $user->pullEvents()[0]);
    }
    

5. Leveraging UUIDv7

  • Default to Uuid::create() for time-sorted IDs:
    $orderId = Uuid::create(); // UUIDv7 (time-ordered)
    
  • Use Uuid::v4() for security-sensitive cases:
    $token = Uuid::v4(); // Random UUIDv4
    

Gotchas and Tips

Pitfalls

1. Event Sourcing Complexity

  • Pitfall: Replaying events to reconstruct state can be slow for large aggregates.
    • Solution: Use event sourcing libraries (e.g., spatie/laravel-event-sourcing) or optimize event storage (e.g., indexing).
  • Pitfall: Handling out-of-order events during replay.
    • Solution: Use snapshotting to store aggregate state periodically.

2. **Consistency Event

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.
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
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