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

Hexagonal Maker Bundle Laravel Package

ahmed-bhs/hexagonal-maker-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Install the bundle:

    composer require ahmed-bhs/hexagonal-maker-bundle --dev
    

    (Auto-registers with Symfony Flex)

  2. Generate a complete module skeleton (e.g., User module):

    bin/console make:hexagonal:crud user/account User
    

    This creates:

    • Domain entity (User)
    • Repository port
    • 5 use cases (CRUD)
    • Controllers
    • Forms
    • Tests
  3. First use case: Implement business logic in the generated RegisterUserCommandHandler (located in src/User/Account/Application/Command/Handler/).

Where to Look First

  • Domain layer: src/User/Account/Domain/ (pure PHP, no framework dependencies)
  • Application layer: src/User/Account/Application/ (use cases, commands, queries)
  • Infrastructure layer: src/User/Account/Infrastructure/ (adapters, Doctrine implementations)
  • Tests: tests/User/Account/ (pre-generated test classes)

First Use Case: Extending the CRUD

Modify the generated RegisterUserCommandHandler to add business rules:

// src/User/Account/Application/Command/Handler/RegisterUserCommandHandler.php
public function __invoke(RegisterUserCommand $command): UserId
{
    $email = new Email($command->email);
    $user = User::register(
        $email,
        $command->password,
        new UserId(Uuid::v7())
    );

    $this->userRepository->save($user);
    return $user->id();
}

Implementation Patterns

Core Workflows

1. Domain-Driven Development (DDD) Workflow

  • Define domain entities first:
    bin/console make:hexagonal:entity user/account User --aggregate-root
    
  • Add value objects for complex data:
    bin/console make:hexagonal:value-object user/account Email
    
  • Implement business logic in domain layer (e.g., User::register()).

2. CQRS Pattern

  • Commands for write operations:
    bin/console make:hexagonal:command user/account register
    
  • Queries for read operations:
    bin/console make:hexagonal:query user/account find-by-id
    
  • Handlers are auto-generated with #[AsMessageHandler] attribute.

3. Repository Pattern

  • Define repository port (interface):
    bin/console make:hexagonal:repository user/account User
    
  • Implement Doctrine adapter in infrastructure:
    // src/User/Account/Infrastructure/Persistence/DoctrineUserRepository.php
    class DoctrineUserRepository implements UserRepository
    {
        public function save(User $user): void
        {
            $this->entityManager->persist($user);
            $this->entityManager->flush();
        }
    }
    

4. Async/Queue Support

  • Generate async message handlers:
    bin/console make:hexagonal:message-handler user/account UserRegistered
    
  • Dispatch commands asynchronously:
    $this->bus->dispatch(
        new RegisterUserCommand($email, $password)
    );
    

Integration Tips

Symfony Forms Integration

  • Generate forms for controllers:
    bin/console make:hexagonal:form user/account User
    
  • Use in controllers:
    $form = $this->createForm(UserType::class, $user);
    

Doctrine ORM Mapping

  • Configure YAML mapping in config/packages/doctrine.yaml:
    doctrine:
        orm:
            mappings:
                UserAccount:
                    type: yaml
                    dir: '%kernel.project_dir%/src/User/Account/Infrastructure/Persistence/Doctrine'
                    prefix: 'App\User\Account\Infrastructure\Persistence\Doctrine'
                    is_bundle: false
    

Testing Patterns

  • Unit tests for domain logic:
    // tests/User/Account/Domain/UserTest.php
    public function testUserRegistration(): void
    {
        $user = User::register(new Email('test@example.com'), 'password123', new UserId());
        $this->assertEquals('test@example.com', $user->email()->value());
    }
    
  • Integration tests for use cases:
    // tests/User/Account/Application/Command/Handler/RegisterUserCommandHandlerTest.php
    public function testRegistration(): void
    {
        $command = new RegisterUserCommand('test@example.com', 'password123');
        $handler = new RegisterUserCommandHandler($this->repository);
        $userId = $handler($command);
        $this->assertInstanceOf(UserId::class, $userId);
    }
    

Event-Driven Architecture

  • Generate domain events:
    bin/console make:hexagonal:domain-event user/account UserRegistered
    
  • Subscribe to events:
    bin/console make:hexagonal:event-subscriber user/account UserRegisteredSubscriber
    

Gotchas and Tips

Pitfalls and Debugging

1. Doctrine Mapping Issues

  • Problem: Generated YAML mapping fails with Class 'App\User\Account\Domain\User' is not a valid entity or mapped super class.
  • Solution: Ensure the entity class is in the correct namespace and the YAML file is placed in Infrastructure/Persistence/Doctrine/.
  • Fix:
    # config/packages/doctrine.yaml
    doctrine:
        orm:
            mappings:
                UserAccount:
                    dir: '%kernel.project_dir%/src/User/Account/Infrastructure/Persistence/Doctrine'
                    prefix: 'App\User\Account\Domain'  # Correct namespace
    

2. Circular Dependencies

  • Problem: #[AsMessageHandler] causes circular dependencies between Command and CommandHandler.
  • Solution: Use autowiring with constructor injection and ensure the CommandBus is properly configured in config/services.yaml:
    services:
        App\User\Account\Application\Command\Handler\RegisterUserCommandHandler:
            arguments:
                $bus: '@messenger.bus.default'
    

3. Value Object Serialization

  • Problem: Value objects (e.g., Email) fail when serialized in Symfony forms or controllers.
  • Solution: Implement __toString() and ensure they are immutable:
    // src/User/Account/Domain/ValueObject/Email.php
    public function __toString(): string
    {
        return $this->value;
    }
    

4. Async Handler Not Triggered

  • Problem: Async message handlers (e.g., UserRegisteredHandler) are not processed.
  • Solution: Ensure the MESSENGER_TRANSPORT_DSN is configured in .env:
    MESSENGER_TRANSPORT_DSN=async://default
    
    And the transport is enabled in config/packages/messenger.yaml:
    messenger:
        transports:
            async: '%env(MESSENGER_TRANSPORT_DSN)%'
    

5. Repository Not Autowired

  • Problem: UserRepository is not injected into the handler.
  • Solution: Use explicit constructor injection and tag the repository service:
    // src/User/Account/Infrastructure/Persistence/DoctrineUserRepository.php
    #[AutoconfigureTag('messenger.message_handler')]
    class DoctrineUserRepository implements UserRepository
    {
        public function __construct(private EntityManagerInterface $entityManager) {}
    }
    

Configuration Quirks

1. Custom Maker Namespaces

  • Problem: Generated classes are placed in incorrect namespaces.
  • Solution: Configure the hexagonal_maker namespace in config/packages/hexagonal_maker.yaml:
    hexagonal_maker:
        namespace:
            domain: 'App\User\Account\Domain'
            application: 'App\User\Account\Application'
            infrastructure: 'App\User\Account\Infrastructure'
    

2. Overriding Default Templates

  • Problem: Default templates (e.g., for commands) don’t match project style.
  • Solution: Override templates in config/packages/hexagonal_maker.yaml:
    hexagonal_maker:
        templates:
            command: 'path/to/custom/command.stub'
            query: 'path/to/custom/query.stub'
    

Extension Points

1. Custom Domain Events

  • Extend the DomainEvent class to add metadata:
    // src/User/Account/Domain/Event/DomainEvent.php
    abstract class DomainEvent
    {
        public function __construct(
            public readonly UserId $userId,
            public readonly DateTimeImmutable $occurredOn
        ) {}
    }
    

2. Custom Value Object Patterns

  • Add new patterns (e.g.,
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.
terminal42/code-quality-tools
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