Srijith Rajamohan, Ph.D.

 

sjster@gmail.com

Machine Learning/ Data Science

Twitter

Mathias Arlaud

mtarld
@matarld
les-tilleuls.coop

Network Map of Entities

Interactive Neighborhood Filtering

Summary

  • 3 Coursera courses on Bayesian Modeling
  • Writing
    •       Articles on ML and DS on Databricks.com
    •       Publish articles on popular Medium publications such as Towards Data Science
    •       Thought-leadership articles
  • Work with Product teams
    •       ML Product team
    •       MLflow team
    •       PySpark team
  • Talks on MLflow, Deep Learning at Databricks, End-to-end ML at Scale, Bayesian Modeling
  • Worked with the SMEs on DL and NLP
  • Helped with the messaging architecture and redesign of the Spark website

Architecture

  • Our tiny panda

  • Doctrine
  • Schema
  • API Platform
  • Resource
  • PHP
  • Object
  • @matarld
  • @chalas_r
  • // src/Domain/Forest/Model/Panda.php
    
    #[ORM\Entity]
    #[ApiResource]
    final class Panda
    {
        public function __construct(
            private UuidInterface $uuid,
            // ...
            #[ORM\Column(type: 'int')]
            private int $hungerAmount,
        ) {
        }
        
        // ...
        
        public function isHungry(): bool
        {
            return $this->hungerAmount > self::HUNGERNESS_THRESHOLD;
        }
    
        public function feed(Bamboo $bamboo): void
        {
            $this->hungerAmount = min(0, $this->hunger - $bamboo->getSize());
        }
    }
    
  • @matarld
  • @chalas_r
  • Decouple from Doctrine

  • #[ORM\Entity]
    #[ApiResource]
    final class Panda
    {
        public function __construct(
            private UuidInterface $uuid,
            // ...
            #[ORM\Column(type: 'int')]
            private int $hunger,
        ) {
        }
        
        // ...
    }
    
  • <!-- src/Infrastructure/Forest/Doctrine/Mapping/Panda.xml -->
    
    <entity name="Acme\Domain\Forest\Model\Panda" table="panda">
    
        <field name="uuid" type="uuid" unique="true" />
        
        <!-- ... -->
        
        <field name="hungry" type="integer" />
     
    </entity>
  • @matarld
  • @chalas_r
  • Decouple from API Platform

  • #[ApiResource]
    final class Panda
    {
        public function __construct(
            private UuidInterface $uuid,
            // ...
            private int $hunger,
        ) {
        }
        
        // ...
    }
    
  • <!-- src/Infrastructure/Forest/ApiPlatform/Resource/Panda.xml -->
    
    <resource class="Acme\Domain\Forest\Model\Panda">
    
      <itemOperations><!-- ... --></itemOperations>
      
      <collectionOperations><!-- ... --></collectionOperations>
     
    </resource>
  • Finding pandas

  • > curl /api/pandas/{uuid}
  • API Platform
  • ReadListener
  • @matarld
  • @chalas_r
  • ChainItemDataProvider
  • [resourceClass, operationName, context]
  • Doctrine
  • ItemDataProvider
  • Finding pandas

  • > curl /api/pandas/{uuid}
  • API Platform
  • ReadListener
  • ChainItemDataProvider
  • [resourceClass, operationName, context]
  • DependencyInjection
  • Compiler Pass
  • Clearing tags
  • Our DataProvider
  • @matarld
  • @chalas_r
  • Command/Query pattern

  • @matarld
  • @chalas_r
  • Command
  •    Domain
  •     Infra
  • Query
  • Query based data provider

  • // src/Application/Forest/Query/FindPandaByUuidQuery.php
    
    final class FindPandaByUuidQuery extends FindByUuidQuery
    {
    }
    
  • FindByUuidQuery
  • @matarld
  • @chalas_r
  • // src/Application/Forest/Query/FindPandaByUuidQueryHandler.php
    
    use Acme\Domain\Forest\Repository\PandaRepository;
    
    final class FindPandaByUuidQueryHandler implements QueryHandler
    {
        public function __construct(private PandaRepository $repository) {}
    
        final public function __invoke(FindPandaByUuidQuery $query): ?Panda
        {
            return $this->repository->searchByUuid($query->uuid);
        }
    }
  • Query based data provider

  • FindPandaByUuidQuery
  • Messenger
  • Query bus
  • Panda
  • A new resource operation attribute
  • @matarld
  • @chalas_r
  • <itemOperation name="get">
      <attribute name="query">FindPandaByUuidQuery</attribute>
    </itemOperation>
    
  • // src/Infrastructure/Shared/ApiPlatform/DataProvider/ItemQueryDataProvider.php
    
    final class ItemQueryDataProvider implements DataProviderInterface
    {
        public function getItem(...): ?object
        {
            $queryClass = $this->resourceMetadataFactory
              ->create($resourceClass)
              ->getItemOperationAttribute($operationName, 'query');
    
            return $this->queryBus->ask(new $queryClass($identifiers['uuid']));
        }
        
        public function supports(...): bool { /* ... */}    
    }
    
  • ItemQueryDataProvider
  • Check that we have a query attribute defined
  • Ensure that's a
  • FindByUuidQuery
  • DependencyInjection
  • Compiler Pass
  • Query based data provider

  • @matarld
  • @chalas_r
  • // src/Infrastructure/Shared/ApiPlatform/DataProvider/CollectionQueryDataProvider.php
    
    final class CollectionQueryDataProvider implements DataProviderInterface
    {
        public function getCollection(...): iterable
        {
            $queryClass = $this->resourceMetadataFactory
              ->create($resourceClass)
              ->getCollectionOperationAttribute($operationName, 'query');
    
            return $this->queryBus->ask($queryClass::fromContext($context));
        }
    }
    
  • <collectionOperation name="get">
      <attribute name="query">FindPandasQuery</attribute>
    </collectionOperation>
    
  • CollectionQueryDataProvider
  • Query based data provider

  • FindPandasQuery
  • Pandas
  • FindByCriteriaQuery
  • @matarld
  • @chalas_r
  • Custom DataProvider
  • Query based data provider

  • FindExtraterrialPandasQuery
  • Custom DataProvider
  • ExtraterrialPandaDataProvider
  • FindPandaByUuidQuery
  • CRUD DataProvider
  • ItemQueryDataProvider
  • // src/Infrastructure/Shared/ApiPlatform/DataPersister/DataPersister.php
    
    final class DataPersister implements DataPersisterInterface
    {
        public function persist(...): ?object
        {
            $commandClass = $this->resourceMetadataFactory->create($resourceClass)
                ->getOperationAttribute($context, 'command');
    
            return $this->commandBus->dispatch($commandClass::fromModel($data));
    
        }
        
        public function remove(...): void
        {
            $commandClass = $this->resourceMetadataFactory->create($resourceClass)
                ->getOperationAttribute($context, 'command');
    
            return $this->commandBus->dispatch($commandClass::fromModel($data));
        }
    }
    
  • DataPersister
  • Command based data persister

  • @matarld
  • @chalas_r
  • PersistCommand
  • RemoveCommand
  • Command based data persister

  • Commands
  • // src/Application/Forest/Command/RemovePandaCommandHandler.php
    
    final class RemovePandaCommandHandler implements CommandHandler
    {
        public function __construct(private PandaRepository $repository) {}
        
        public function __invoke(RemovePandaCommand $command): void
        {
            $this->repository->remove($command->id());
        }
    }
    
  • @matarld
  • @chalas_r
  • // src/Application/Forest/Command/RemovePandaCommand.php
    
    final class RemovePandaCommand implements RemoveCommand
    {
        public function __construct(private int $id) {}
        
        public static function fromModel(object $panda): self
        {
            return new self($panda->id());
        }
    }
    
  • Command based data persister

  • Commands
  • @matarld
  • @chalas_r
  • <itemOperation name="remove">
      <attribute name="query">FindPandaByUuidQuery</attribute>
      <attribute name="command">RemovePandaCommand</attribute>
    </itemOperation>
    
  • Delete it
  • Find a panda by its uuid
  • In a nutshell

  • ItemQueryDataProvider
  • FindByUuidQuery
  • CollectionQueryDataProvider
  • FindByCriteriaQuery
  • DataPersister
  • PersistCommand
  • DataPersister
  • RemoveCommand
  • API Platform
  • ReadListener
  • API Platform
  • WriteListener
  • @matarld
  • @chalas_r
  • Anemic models

  • Use case
  • final class Panda
    {
        public function __construct(private string $name)
        {
        }
        
        public function getName(): string
        {
            return $this->name;
        }
        
        public function setName(string $name): void
        {
            $this->name = $name;
        }
    }
    
  • {"name": "Pedro Panda"}
  • PATCH
  • Symfony
  • PropertyAccessor
  • Public properties
  • Setters
  • Constructor arguments
  • final class Panda
    {
        public function __construct(private string $name)
        {
        }
        
        public function name(): string
        {
            return $this->name;
        }
        
        public function rename(string $firstname, string $lastname): void
        {
            $this->name = sprintf('%s %s', $firstname, $lastname);
        }
    }
    
  • Public properties
  • Setters
  • Constructor arguments
  • @matarld
  • @chalas_r
  • Rich models

  • DTOs

  • Data Transfer Objects
  • @matarld
  • @chalas_r
  • DTOs

  • Payload and views
  • {
      "uuid": "...",
      "name": "Pedro Panda",
      "hungry": true
    }
  • {
      "firstname": "Pedro",
      "lastname": "Panda"
    }
  • final class Panda
    {
        public function __construct(
            private UuidInterface $uuid,
            private string $name,
            private int $hunger,
        ) {
        }
        
        // ...
    }
    
  • Payload
  • Model
  • View
  • Concatenate firstname and lastname
  • Convert hunger to "hungry" boolean
  • @matarld
  • @chalas_r
  • DTOs

  • API Platform configuration
  • <itemOperation name="get">
      
      <attribute name="input">Acme\Application\Forest\Payload\PandaPayload</attribute>
      <attribute name="output">Acme\Application\Forest\View\PandaView</attribute>
      
    </itemOperation>
  • API Platform
  • DeserializeListener
  • API Platform
  • SerializeListener
  • @matarld
  • @chalas_r
  • Payload

  • Input DTO
  • // src/Application/Forest/Payload/PandaPayload.php
    
    final class PandaPayload
    {
        public function __construct(
            public readonly ?string $firstname = null,
            public readonly ?string $lastname = null,
        ) {
        }
    }
    
  • // src/Domain/Forest/Model/Panda.php
    
    final class Panda
    {
        public function __construct(private string $name)
        {
        }
        
        public function rename(
            string $firstname, 
            string $lastname,
        ): void {
            $this->name = sprintf('%s %s', $firstname, $lastname);
        }
    }
    
  • PandaPayloadDataTransformer
  • Use rename method
  • @matarld
  • @chalas_r
  • Payload

  • PayloadDataTransformer
  • // src/Infrastructure/Forest/ApiPlatform/DataTransformer/PandaPayloadDataTransformer.php
    
    final class PandaPayloadDataTransformer implements DataTransformerInterface
    {
        public function transform($payload, string $to, array $context = []) {
            $panda = $context['object_to_populate'] ?? new Panda();
            $panda->rename($payload->firstname, $payload->lastname);
    
            return $panda;
        }
    
        public function supportsTransformation($data, string $to, array $context = []): bool {
            return PandaPayload::class === ($context['input']['class'] ?? null)
                && Panda::class === $to;
        }
    }
    
  • @matarld
  • @chalas_r
  • View

  • Output DTO
  • // src/Domain/Forest/Model/Panda.php
    
    final class Panda
    {
        public function __construct(
            private UuidInterface $uuid 
            private string $name,
            private int $hunger,
        ) {
        }
        
        public function isHungry(): bool
    	{
            return $this->hunger > self::HUNGERNESS_THRESHOLD;
        }
    }
    
  • // src/Application/Forest/View/PandaView.php
    
    final class PandaView
    {
        public function __construct(
            public readonly string $uuid,
            public readonly string $name,
            public readonly bool $hungry,
        ) {
        }
    }
    
  • PandaViewDataTransformer
  • Cast uuid, use isHungry method
  • @matarld
  • @chalas_r
  • View

  • ViewDataTransformer
  • // src/Infrastructure/Forest/ApiPlatform/DataTransformer/PandaViewDataTransformer.php
    
    final class PandaViewDataTransformer implements DataTransformer
    {
        public function transform($panda, string $to, array $context = [])
        {
            return PandaView((string) $panda->uuid(), $panda->name(), $panda->isHungry());
        }
    
        public function supportsTransformation($data, string $to, array $context = []): bool
        {
            return PandaView::class === $to && $data instanceof Panda;
        }
    }
  • @matarld
  • @chalas_r
  • To Summarize

  • @matarld
  • @chalas_r
  • API Platform is fully extensible.
  • Don't do this for your personal blog!
  • Take back control over the domain
  • Embrace complexity
  • Write more scalable & testable code
  • To Summarize

  • @matarld
  • @chalas_r
  • Thanks!

  • @matarld

  • @chalas_r

  • @coopTilleuls

Copy of [Forum PHP] DDD & APIP

By sjster

Copy of [Forum PHP] DDD & APIP

  • 151