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

Common Laravel Package

dddominio/common

Common utilities for the DDDominio ecosystem: shared helpers, base classes, and cross-package abstractions to standardize behavior and reduce duplication across domain-driven Laravel/PHP packages.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require dddominio/common
    

    Verify compatibility with your Laravel version (e.g., PHP 8.1+, Laravel 9+) in composer.json.

  2. First Use Case: Immutable Value Objects Define a value object (e.g., Money) in your domain layer:

    use DDDominio\Common\ValueObject;
    
    class Money extends ValueObject
    {
        public function __construct(
            public string $amount,
            public string $currency
        ) {
            $this->validate();
        }
    
        protected function validate(): void
        {
            if ($this->amount <= 0) {
                throw new \InvalidArgumentException("Amount must be positive.");
            }
        }
    
        public function equals(Money $other): bool
        {
            return $this->amount === $other->amount &&
                   $this->currency === $other->currency;
        }
    }
    
    • Key: Extend ValueObject and implement equals() for domain-specific comparison logic.
  3. Second Use Case: Entity with Domain Events Create an entity (e.g., Order) that emits events:

    use DDDominio\Common\Entity;
    use DDDominio\Common\Domain\Event\DomainEvent;
    
    class Order extends Entity
    {
        public function __construct(
            public string $id,
            public Money $total,
            public array $items = []
        ) {
            $this->recordThat(new OrderCreated($id, $total));
        }
    
        public function addItem(Product $product): void
        {
            $this->items[] = $product;
            $this->recordThat(new ItemAdded($this->id, $product->id));
        }
    }
    
    • Key: Use recordThat() to publish domain events. Configure event dispatching in Laravel’s EventServiceProvider or via the package’s config.
  4. Third Use Case: Repository Interface Define a repository contract for your aggregate root (e.g., Order):

    use DDDominio\Common\Repository\RepositoryInterface;
    
    class OrderRepository implements RepositoryInterface
    {
        public function find(string $id): ?Order
        {
            // Implement using Eloquent or custom logic
            return Order::query()->find($id);
        }
    
        public function persist(Order $order): void
        {
            $order->save();
        }
    }
    
    • Key: Bind the repository to Laravel’s container in AppServiceProvider:
      $this->app->bind(OrderRepository::class, function ($app) {
          return new OrderRepository(new OrderModel());
      });
      
  5. Where to Look First

    • Package Source: Browse src/Domain for core classes (Entity.php, ValueObject.php).
    • Tests: Check tests for usage examples (e.g., EntityTest.php).
    • Config: Look for config/dddominio.php (if published) for event dispatching or repository defaults.

Implementation Patterns

Usage Patterns

  1. Domain Layer Structure Organize code by bounded contexts (e.g., app/Domain/Order, app/Domain/User):

    /Domain
      /Order
        - Order.php          (AggregateRoot)
        - OrderCreated.php   (DomainEvent)
        - Money.php          (ValueObject)
        - OrderRepository.php
      /User
        - User.php
        - Email.php          (ValueObject)
    
  2. Entity Lifecycle

    • Creation: Use constructors to enforce invariants (e.g., Money validation).
    • Events: Delegate side effects to domain events (e.g., OrderCreated triggers a notification).
    • Persistence: Delegate to repositories (e.g., OrderRepository::persist($order)).
  3. Value Objects

    • Immutability: All properties should be readonly or set only in constructors.
    • Equality: Override equals() for domain-specific comparisons (e.g., Money ignores minor currency fluctuations).
    • Validation: Move business rules to validate() methods.
  4. Repositories

    • Interface Segregation: Extend RepositoryInterface for aggregate roots only.
    • Laravel Integration: Use Eloquent models as data mappers:
      class OrderRepository implements RepositoryInterface
      {
          public function __construct(private OrderModel $model) {}
      
          public function find(string $id): ?Order
          {
              return $this->model->find($id)?->toDomain();
          }
      }
      
    • Unit of Work: For transactions, use Laravel’s DB facade or a custom UnitOfWork pattern.
  5. Domain Events

    • Dispatching: Use Laravel’s event system or a custom dispatcher:
      // Option 1: Laravel Events
      event(new OrderCreated($order->id));
      
      // Option 2: DDDominio Dispatcher (if supported)
      $this->eventDispatcher->dispatch($event);
      
    • Listening: Subscribe to events in EventServiceProvider:
      protected $listen = [
          OrderCreated::class => [
              SendOrderConfirmation::class,
          ],
      ];
      
  6. Services

    • Domain Services: Use plain PHP classes for stateless logic (e.g., OrderCalculator).
    • Application Services: Use Laravel controllers/commands to orchestrate domain logic:
      class CreateOrderService
      {
          public function __construct(
              private OrderRepository $repository,
              private EventDispatcher $dispatcher
          ) {}
      
          public function execute(array $data): Order
          {
              $order = new Order($data['id'], new Money($data['total']));
              $this->repository->persist($order);
              return $order;
          }
      }
      

Workflows

  1. New Feature Development

    • Step 1: Model the domain (e.g., Order, Money).
    • Step 2: Define events (e.g., OrderCreated, PaymentProcessed).
    • Step 3: Implement repositories and bind to Laravel.
    • Step 4: Write application services/controllers to orchestrate workflows.
  2. Testing

    • Unit Tests: Mock repositories and test domain logic in isolation:
      $repository = Mockery::mock(OrderRepository::class);
      $repository->shouldReceive('find')->andReturn($order);
      $this->assertTrue($order->isPaid());
      
    • Integration Tests: Test event dispatching and repository interactions:
      $this->assertDatabaseHas('orders', ['id' => $order->id]);
      $this->assertEquals(1, OrderCreated::dispatches()->count());
      
  3. Migration from Eloquent

    • Step 1: Extract domain logic from Eloquent models into Entity/ValueObject classes.
    • Step 2: Create repository adapters for Eloquent models.
    • Step 3: Gradually replace Eloquent usage with domain objects.

Integration Tips

  1. Laravel Eloquent

    • Use Eloquent models as data mappers for repositories:
      class OrderModel extends Model
      {
          public function toDomain(): Order
          {
              return new Order(
                  $this->id,
                  new Money($this->total_amount, $this->currency)
              );
          }
      }
      
    • Avoid mixing domain logic (e.g., validation) in Eloquent models.
  2. Validation

    • Use Laravel’s FormRequest for API input validation, then validate again in domain objects:
      // API Layer
      $request->validate(['total' => 'required|numeric']);
      
      // Domain Layer
      $money = new Money($request->total, 'USD'); // Throws if invalid
      
  3. APIs

    • Transform domain objects to JSON using Laravel’s resources:
      class OrderResource extends JsonResource
      {
          public function toArray($request)
          {
              return [
                  'id' => $this->resource->id,
                  'total' => $this->resource->total->amount,
              ];
          }
      }
      
  4. CQRS (Optional)

    • For read models, use Laravel’s query builder or a separate ReadModel package:
      class OrderReadModel
      {
          public function findById(string $id): array
          {
              return Order::query()
                  ->where('id', $id)
                  ->with('items')
                  ->first()
                  ->toArray();
          }
      }
      

Gotchas and Tips

Pitfalls

  1. Over-Engineering

    • Risk: Applying DDD patterns (e.g., aggregates, repositories) to simple domains.
    • Fix: Start with value objects/entities, then introduce aggregates/repositories only when needed.
  2. Repository Implementation Leaks

    • Risk: Tight coupling between repositories and Laravel’s Eloquent (e.g., hardcoding
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.
besmartand-pro/php-quality-config
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