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

alexandrebulete/ddd-doctrine-bridge

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require alexandrebulete/ddd-doctrine-bridge
    

    Ensure doctrine/orm and spatie/laravel-data (or similar DDD support packages) are installed.

  2. Basic Configuration

    • No explicit config file is required, but ensure Doctrine ORM is properly bootstrapped in Laravel (via config/database.php and config/doctrine.php).
    • Register the bridge service provider in config/app.php:
      'providers' => [
          // ...
          Alexandrebulete\DddDoctrineBridge\DddDoctrineBridgeServiceProvider::class,
      ],
      
  3. First Use Case: Mapping a DDD Entity to a Doctrine Entity

    use Alexandrebulete\DddDoctrineBridge\Attributes\DoctrineEntity;
    use Alexandrebulete\DddDoctrineBridge\DoctrineEntityMapper;
    
    #[DoctrineEntity]
    class UserEntity {
        // Your DDD entity properties/methods
    }
    
    // Map to Doctrine entity
    $mapper = app(DoctrineEntityMapper::class);
    $doctrineEntity = $mapper->mapToDoctrineEntity(new UserEntity());
    

Implementation Patterns

1. Entity Mapping Workflows

  • Automatic Mapping: Use attributes (#[DoctrineEntity], #[DoctrineColumn]) to define how DDD entities map to Doctrine entities. Example:
    #[DoctrineEntity(repository: UserRepository::class)]
    class User {
        #[DoctrineColumn(name: 'email', type: 'string')]
        public string $email;
    }
    
  • Manual Mapping: Override default behavior via DoctrineEntityMapper:
    $mapper->mapToDoctrineEntity($dddEntity, CustomDoctrineEntity::class);
    

2. Repository Integration

  • Replace Laravel Eloquent repositories with Doctrine repositories:
    use Alexandrebulete\DddDoctrineBridge\DoctrineRepository;
    
    class UserRepository extends DoctrineRepository {
        protected $entityClass = User::class;
    }
    
  • Inject the repository via Laravel’s service container:
    $repository = app(UserRepository::class);
    $users = $repository->findAll();
    

3. Domain Events and Doctrine Lifecycle

  • Dispatch DDD domain events after Doctrine operations:
    use Alexandrebulete\DddDoctrineBridge\Event\DoctrineEntityPersisted;
    
    DoctrineEntityPersisted::dispatch($doctrineEntity);
    
  • Listen to Doctrine events (e.g., prePersist, postUpdate) and trigger DDD logic:
    DoctrineEntityManager::getEventManager()->addEventListener(
        ['prePersist', 'preUpdate'],
        new DddDomainEventListener()
    );
    

4. Value Object Handling

  • Map DDD value objects to Doctrine embeddable types:
    #[DoctrineEmbeddable]
    class Address {
        public string $street;
    }
    
    #[DoctrineEntity]
    class User {
        #[DoctrineEmbedded]
        public Address $address;
    }
    

5. Aggregates and Unit of Work

  • Use Doctrine’s UnitOfWork to manage aggregate roots:
    $entityManager = DoctrineEntityManager::get();
    $uow = $entityManager->getUnitOfWork();
    $uow->registerManaged($doctrineEntity, $dddAggregateRoot);
    

Gotchas and Tips

Pitfalls

  1. Attribute Overrides:

    • Attributes like #[DoctrineColumn] take precedence over Doctrine’s default naming conventions. Ensure consistency to avoid runtime errors.
    • Example: Omitting name in #[DoctrineColumn] may lead to unexpected column names.
  2. Circular Dependencies:

    • DDD entities referencing each other (e.g., OrderCustomer) can cause infinite loops in mapping. Use #[DoctrineIgnore] to exclude problematic properties:
      #[DoctrineIgnore]
      public Order $order;
      
  3. Transaction Boundaries:

    • Doctrine transactions may not align with DDD transaction scripts. Explicitly manage transactions:
      $entityManager->beginTransaction();
      try {
          $entityManager->persist($doctrineEntity);
          $entityManager->flush();
          $entityManager->commit();
      } catch (\Exception $e) {
          $entityManager->rollback();
          throw $e;
      }
      
  4. Lazy Loading Conflicts:

    • Doctrine’s lazy loading may interfere with DDD lazy-loaded collections. Use #[DoctrineFetch("EAGER")] to force eager loading:
      #[DoctrineFetch("EAGER")]
      public Collection $orders;
      

Debugging Tips

  1. Enable Doctrine Logging: Add to config/doctrine.php:

    'logging' => true,
    'logging_level' => \Doctrine\ORM\Logging\LogLevel::DEBUG,
    

    Logs appear in Laravel’s log channel.

  2. Validate Mappings: Use the DoctrineEntityMapper::validateMapping() method to check for inconsistencies before runtime:

    $mapper->validateMapping(User::class);
    
  3. Hybrid Repositories: If mixing Eloquent and Doctrine, ensure repositories are type-hinted correctly to avoid ambiguity:

    // Avoid:
    public function find(int $id) { ... }
    // Prefer:
    public function find(User $user) { ... }
    

Extension Points

  1. Custom Mappers: Extend DoctrineEntityMapper to handle bespoke DDD-to-Doctrine logic:

    class CustomMapper extends DoctrineEntityMapper {
        protected function mapProperty($dddProperty, $doctrineProperty) {
            // Custom logic here
        }
    }
    
  2. Event Subscribers: Create custom Doctrine event subscribers for DDD-specific behaviors:

    use Doctrine\ORM\Event\LifecycleEventArgs;
    
    class DddLifecycleSubscriber implements \Doctrine\Common\EventSubscriber {
        public function postPersist(LifecycleEventArgs $args) {
            $entity = $args->getObject();
            // Trigger DDD domain events
        }
    }
    
  3. Query Builders: Override Doctrine’s query builder to support DDD query objects:

    $queryBuilder = $entityManager->createQueryBuilder();
    $queryBuilder->andWhere('u.status = :status')
                 ->setParameter('status', UserStatus::ACTIVE);
    
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
codifyo/ts-generator-bundle
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