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

alexandrebulete/ddd-apiplatform-bridge

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require alexandrebulete/ddd-apiplatform-bridge
    

    Ensure api is in your providers array in config/app.php and ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle is registered.

  2. Basic Configuration Add the bridge service to your DDD domain layer by extending Domain\DomainServiceProvider (or equivalent):

    use Alexandrebulete\DddApiPlatformBridge\Domain\ApiPlatformDomainService;
    
    public function register()
    {
        $this->app->singleton(ApiPlatformDomainService::class, function ($app) {
            return new ApiPlatformDomainService(
                $app->make(\ApiPlatform\Core\Api\UrlGeneratorInterface::class),
                $app->make(\ApiPlatform\Core\Api\IriConverterInterface::class)
            );
        });
    }
    
  3. First Use Case: Domain-to-API Mapping Inject ApiPlatformDomainService into your domain service and use it to generate IRIs or URLs:

    use Alexandrebulete\DddApiPlatformBridge\Domain\ApiPlatformDomainService;
    
    class OrderService
    {
        public function __construct(
            private ApiPlatformDomainService $apiPlatformDomainService
        ) {}
    
        public function getOrderUrl(Order $order): string
        {
            return $this->apiPlatformDomainService->generateUrl(
                'api_order_item_collection',
                ['order_id' => $order->getId()]
            );
        }
    }
    
  4. Key Files to Review

    • src/Domain/ApiPlatformDomainService.php (core logic)
    • src/Infrastructure/ApiPlatform/ApiPlatformResourceFactory.php (resource creation)
    • tests/ (for edge cases and expected behavior)

Implementation Patterns

Domain-Driven Design (DDD) Integration

  1. Domain Layer Abstraction Use ApiPlatformDomainService to decouple domain logic from API concerns:

    // Domain Service (no API dependency)
    class OrderDomainService {
        public function createOrder(OrderData $data): Order {
            // Business logic
        }
    }
    
    // Application Layer (API-aware)
    class OrderApplicationService {
        public function __construct(
            private OrderDomainService $domainService,
            private ApiPlatformDomainService $apiPlatformDomainService
        ) {}
    
        public function handle(OrderRequest $request): OrderDto {
            $order = $this->domainService->createOrder($request->toDomainData());
            return new OrderDto(
                $order,
                $this->apiPlatformDomainService->generateIri($order)
            );
        }
    }
    
  2. Resource Creation Pattern Dynamically create API resources from domain entities:

    use Alexandrebulete\DddApiPlatformBridge\Infrastructure\ApiPlatform\ApiPlatformResourceFactory;
    
    $resourceFactory = new ApiPlatformResourceFactory();
    $resource = $resourceFactory->createFromDomain($order, OrderResource::class);
    
  3. Event-Driven Workflows Trigger API operations (e.g., notifications) from domain events:

    class OrderCreatedEventHandler {
        public function __construct(
            private ApiPlatformDomainService $apiPlatformDomainService
        ) {}
    
        public function __invoke(OrderCreatedEvent $event): void {
            $url = $this->apiPlatformDomainService->generateUrl(
                'api_order_show',
                ['id' => $event->getOrderId()]
            );
            // Send email with $url
        }
    }
    
  4. Validation Bridge Reuse domain validation rules in API Platform:

    # config/packages/api_platform.yaml
    api_platform:
        formats:
            jsonld:
                validation_context:
                    groups: ['domain', 'api']
    

API Platform Integration

  1. State Preservation Use the bridge to preserve domain state in API responses:

    class OrderResource extends JsonLDResource {
        public function getAdditionalContext(): array
        {
            return [
                '@id' => $this->getContextBuilder()->getIriConverter()->getIriFromResource($this->data),
                '@type' => 'Order',
                'status' => $this->data->getStatus()->value,
            ];
        }
    }
    
  2. Custom Operations Define domain-aware API operations:

    # config/api_platform/resources.yaml
    App\Entity\Order:
        collectionOperations:
            create_order:
                method: 'POST'
                path: '/orders'
                controller: App\Controller\OrderDomainController::createAction
                deserializationContext:
                    groups: ['order:create']
    
  3. Serialization Groups Align API serialization with domain layers:

    // Domain Entity
    #[Groups(['order:read'])]
    public function getCustomerName(): string { ... }
    
    // API Resource
    #[Groups(['order:api'])]
    public function getCustomer(): CustomerDto { ... }
    
  4. Pagination Control Delegate pagination to domain services:

    class OrderCollection {
        public function __construct(
            private ApiPlatformDomainService $apiPlatformDomainService,
            private OrderRepository $repository
        ) {}
    
        public function getItems(int $page = 1): array
        {
            $offset = ($page - 1) * 20;
            return $this->repository->findBy([], null, 20, $offset);
        }
    }
    

Gotchas and Tips

Pitfalls

  1. Circular Dependencies

    • Issue: Injecting ApiPlatformDomainService into domain entities can violate DDD purity.
    • Fix: Restrict usage to application/services layers. Domain entities should remain API-agnostic.
  2. IRI Generation Edge Cases

    • Issue: generateIri() may fail if the resource isn’t registered with API Platform.
    • Fix: Validate resource existence first:
      if (!$this->apiPlatformDomainService->isResourceRegistered(Order::class)) {
          throw new \RuntimeException('Resource not configured in API Platform.');
      }
      
  3. State Inconsistency

    • Issue: Domain state may diverge from API state if not synchronized.
    • Fix: Use domain events to propagate changes:
      $order->addEvent(new OrderStatusChangedEvent($order->getId(), $newStatus));
      
  4. Performance Overhead

    • Issue: Dynamic resource creation (ApiPlatformResourceFactory) can be slow for bulk operations.
    • Fix: Cache factory instances or pre-generate resources.

Debugging

  1. Resource Not Found

    • Check if the resource class is properly annotated with @ApiResource.
    • Verify the route name matches the one used in generateUrl().
  2. Serialization Errors

    • Enable API Platform debug mode:
      APP_DEBUG=1 APP_ENV=dev php bin/console debug:api
      
    • Look for missing serialization groups or circular references.
  3. Event Dispatching

    • Use Symfony’s event dispatcher debug command:
      php bin/console debug:event-dispatcher
      
    • Ensure domain events are tagged correctly for API Platform listeners.

Configuration Quirks

  1. Custom URL Generators

    • Override the default URL generator by binding your own:
      $this->app->bind(\ApiPlatform\Core\Api\UrlGeneratorInterface::class, function ($app) {
          return new CustomUrlGenerator($app->make('router'));
      });
      
  2. Dynamic Route Names

    • Use placeholders for dynamic route names:
      $url = $this->apiPlatformDomainService->generateUrl(
          'api_{resource}_collection',
          ['resource' => 'order']
      );
      
  3. Context-Specific IRIs

    • Pass custom contexts to generateIri():
      $iri = $this->apiPlatformDomainService->generateIri(
          $order,
          ['groups' => ['order:api']]
      );
      

Extension Points

  1. Custom Domain Services Extend ApiPlatformDomainService to add domain-specific logic:

    class CustomApiPlatformDomainService extends ApiPlatformDomainService {
        public function generateOrderUrl(Order $order): string {
            return $this->generateUrl(
                'api_order_show',
                ['id' => $order->getId(), 'version' => $order->getVersion()]
            );
        }
    }
    
  2. Resource Factories Create domain-specific factories:

    class OrderResourceFactory extends ApiPlatformResourceFactory {
        public function createFromDomain(Order $order): OrderResource {
            $resource = parent::createFromDomain($order, OrderResource::class);
            $resource->setStatus($order->getStatus()->value);
            return $resource;
        }
    }
    
  3. Event Listeners Add domain-aware API Platform listeners:

    class OrderApiEventSubscriber implements EventSubscriberInterface {
        public static function getSubscribedEvents(): array {
            return [
                KernelEvents::VIEW => ['onKernelView', 10],
            ];
    
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