Application Layer in PHP
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Application Layer in PHP

am

  • 683 Views

Vortrag über eine beispielhafte Implementierung eines Application Layers in PHP

Vortrag über eine beispielhafte Implementierung eines Application Layers in PHP

Statistiken

Views

Gesamtviews
683
Views auf SlideShare
683
Views einbetten
0

Actions

Gefällt mir
0
Downloads
4
Kommentare
0

0 Einbettungen 0

No embeds

Zugänglichkeit

Kategorien

Details hochladen

Uploaded via as Adobe PDF

Benutzerrechte

© Alle Rechte vorbehalten

Report content

Als unangemessen gemeldet Als unangemessen melden
Als unangemessen melden

Wählen Sie Ihren Grund, warum Sie diese Präsentation als unangemessen melden.

Löschen
  • Full Name Full Name Comment goes here.
    Sind Sie sicher, dass Sie...
    Ihre Nachricht erscheint hier
    Processing...
Kommentar posten
Kommentar bearbeiten

Application Layer in PHP Presentation Transcript

  • 1. Application Layer ein Vortrag von Per Bernhardt
  • 2. Agenda • Disclaimer / Credits • Ein kleines bisschen Theorie • Ein echtes Beispiel: Chefkoch API
  • 3. Credit goes to… • http://martinfowler.com/ • http://domainlanguage.com/ • http://alistair.cockburn.us/ • http://www.whitewashing.de/ • und viele andere..
  • 4. Ein bisschen Theorie…
  • 5. Application Layer Presentation Layer Infrastructure Layer Domain Layer
  • 6. Application Layer Presentation Layer Infrastructure Layer Domain Layer - Controller - Template / HTML - Session / HTTP
  • 7. Application Layer Presentation Layer Infrastructure Layer Domain Layer - Controller - Template / HTML - Session / HTTP - Fasade - Transaktionen - Sicherheit / Zugriffsschutz - Integration / Orchestrierung
  • 8. Application Layer Presentation Layer Infrastructure Layer Domain Layer - Controller - Template / HTML - Session / HTTP - Fasade - Transaktionen - Sicherheit / Zugriffsschutz - Integration / Orchestrierung - Daten - Geschäftslogik
  • 9. Application Layer Presentation Layer Infrastructure Layer Domain Layer - Controller - Template / HTML - Session / HTTP - Fasade - Transaktionen - Sicherheit / Zugriffsschutz - Integration / Orchestrierung - Daten - Geschäftslogik - Datenbank - Mailserver - Logger - …
  • 10. Application Layer Presentation Layer Infrastructure Layer Domain Layer - Controller - Template / HTML - Session / HTTP - Fasade - Transaktionen - Sicherheit / Zugriffsschutz - Integration / Orchestrierung - Daten - Geschäftslogik - Datenbank - Mailserver - Logger - …
  • 11. Application Layer Presentation Layer Infrastructure Layer Domain Layer - Controller - Template / HTML - Session / HTTP - Fasade - Transaktionen - Sicherheit / Zugriffsschutz - Integration / Orchestrierung - Daten - Geschäftslogik - Datenbank - Mailserver - Logger - …
  • 12. Chefkoch API
  • 13. POST /cookbooks/{id}/categories
  • 14. <?php ! namespace ChefkochBundleApiBundleController; ! use SymfonyBundleFrameworkBundleControllerController; use ChefkochDomainModelCookbookCookbookId use SensioBundleFrameworkExtraBundleConfigurationParamConverter; use ChefkochBundleApiBundleAnnotationApiSerialize; use ChefkochApplicationCookbookRequest; use ChefkochApplicationCookbookCommand; use PixelhouseApplicationEventDispatchingService; use PixelhouseApplicationResponse; ! class CookbookController extends Controller { ! ... ! /** * @ParamConverter("category", options={"deserialize"=true}) * @ApiSerialize * @return Response */ public function saveCategoryAction(CookbookId $cookbookId, RequestCategoryRequest $category) { return $this->getCookbookService()->execute( new CommandSaveCategoryCommand($cookbookId, $category) ); } ! ... ! /** * @return DispatchingService */ private function getCookbookService() { return $this->get('chefkoch_api.application.cookbook_service'); } }
  • 15. <?php ! namespace ChefkochApplicationCookbookCommand; ! use ChefkochApplicationCookbookRequestCategoryRequest; use ChefkochApplicationCookbookSecurityCookbookWriteAccessRequired; use ChefkochDomainModelCookbookCookbookId; use PixelhouseApplicationCommand; ! class SaveCategoryCommand implements Command, CookbookWriteAccessRequired { ! /** @var CookbookId */ private $cookbookId; ! /** @var CategoryRequest */ private $categoryRequest; ! public function __construct(CookbookId $cookbookId, CategoryRequest $categoryRequest) { $this->cookbookId = $cookbookId; $this->categoryRequest = $categoryRequest; } ! public function getCookbookId() { return $this->cookbookId; } ! public function getCategoryRequest() { return $this->categoryRequest; } }
  • 16. <?php ! namespace PixelhouseApplicationEvent; ! use PixelhouseApplicationCommand; use PixelhouseApplicationUseCase; use PixelhouseEventDispatcherEventDispatcher; ! class DispatchingService { ! /** @var EventDispatcher */ private $eventDispatcher; ! /** @var UseCase[] */ private $useCases = array(); ! public function __construct(EventDispatcher $eventDispatcher) { $this->eventDispatcher = $eventDispatcher; } ! public function registerCommand($commandClass, UseCase $useCase) { $this->useCases[$commandClass] = $useCase; } ! public function execute(Command $command) { ... } }
  • 17. <?php ! namespace PixelhouseApplicationEvent; ! class Events { const PRE_COMMAND = 'application.pre_command'; const POST_COMMAND = 'application.post_command'; const EXCEPTION = 'application.exception'; }
  • 18. <?php ! namespace PixelhouseApplicationEvent; ! use PixelhouseApplicationCommand; ! class DispatchingService { ! ... ! public function execute(Command $command) { try { $this->eventDispatcher->dispatch( Events::PRE_COMMAND, new CommandEvent($command) ); $response = $this->useCases[get_class($command)]->run($command); $this->eventDispatcher->dispatch( Events::POST_COMMAND, new PostCommandEvent($command, $response) ); ! return $response; } catch (Exception $exception) { $event = new CommandExceptionEvent($command, $exception); $this->eventDispatcher->dispatch( Events::EXCEPTION, $event ); if ($response = $event->getResponse()) { return $response; } else { throw $exception; } } } }
  • 19. <?php ! namespace ChefkochInfrastructureApplication; ! use PixelhouseApplicationEvent; use PixelhouseEventDispatcherSubscriber; use DoctrineORMEntityManager; ! class DoctrineTransactionListener implements Subscriber { ! /** @var EntityManager */ private $entityManager; ! public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; } ! public function preCommand(EventCommandEvent $event) { $this->entityManager->getConnection()->beginTransaction(); } ! public function postCommand(EventPostCommandEvent $event) { $this->entityManager->flush(); $this->entityManager->getConnection()->commit(); } ! public function onException(EventCommandExceptionEvent $event) { $this->entityManager->close(); if ($this->entityManager->getConnection()->isTransactionActive()) { $this->entityManager->getConnection()->rollBack(); } } }
  • 20. <?php ! namespace PixelhouseApplicationSecurity; ! use PixelhouseApplicationEvent; use PixelhouseEventDispatcherSubscriber; ! class SecurityListener implements Subscriber { ! /** @var Context */ private $context; ! /** @var Policy[] */ private $policies = array(); ! public function __construct(Context $context) { $this->context = $context; } ! public function addPolicy(Policy $policy) { $this->policies[] = $policy; } ! /** * throws Exception */ public function preCommand(EventCommandEvent $event) { foreach ($this->policies as $policy) { $policy->check($this->securityContext, $event->getCommand()); } } }
  • 21. <?php ! namespace ChefkochApplicationCookbookSecurity; ! use ChefkochDomainModelCookbookCookbookRepository; use ChefkochUserDomainModelUserRepository; use PixelhouseApplicationCommand; use PixelhouseApplicationSecurityAccessDeniedException; use PixelhouseApplicationSecurityContext; use PixelhouseApplicationSecurityPolicy; ! class CookbookAccessPolicy implements Policy { ! /** @var CookbookRepository */ private $cookbookRepository; ! /** @var UserRepository */ private $userRepository; ! public function __construct(CookbookRepository $cbRepo, UserRepository $uRepo) { $this->cookbookRepository = $cbRepo; $this->userRepository = $uRepo; } ! public function check(Context $context, Command $command) { if ($command instanceof CookbookWriteAccessRequired) { $cookbook = $this->cookbookRepository->findOneById($command->getCookbookId()); $user = $this->userRepository->findOneById($context->getUserId()); ! // Zugriff prüfen ... ! throw new AccessDeniedException(); } } }
  • 22. <?xml version="1.0" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services ..."> <services> ... ! <service id="chefkoch_api.application.use_case.cookbook_save_category" class="ChefkochApplicationCookbookUseCaseSaveCategory"> <argument type="service" id="chefkoch_api.infrastructure.cookbook_category_repository" /> <argument type="service" id="chefkoch_api.infrastructure.user_repository" /> <argument type="service" id="chefkoch_api.application.security_context" /> <tag name="chefkoch_api.application.use_case" commandClass="ChefkochApplicationCookbookCommandSaveCategoryCommand" applicationService="chefkoch_api.application.cookbook_service" /> </service> ! ... </services> </container>
  • 23. <?php ! namespace ChefkochApplicationCookbookUseCase; ! use ChefkochApplicationCookbookCommandSaveCategoryCommand; use PixelhouseApplicationSecurityContext; use PixelhouseApplicationUseCase; use ChefkochDomainModelCookbookCategoryRepository; use ChefkochUserDomainModelUserRepository; ! class SaveCategory implements UseCase { ! /** @var CategoryRepository */ private $categoryRepository; ! /** @var UserRepository */ private $userRepository; ! /** @var Context */ private $context; ! public function __construct(CategoryRepository $cRepo, UserRepository $uRepo, Context $context) { $this->categoryRepository = $cRepo; $this->userRepository = $uRepo; $this->context = $context; } ! public function run(SaveCategoryCommand $command) { ... } }
  • 24. <?php ! namespace ChefkochApplicationCookbookUseCase; ! use PixelhouseApplicationUseCase; use ChefkochApplicationCookbookCommandSaveCategoryCommand; use ChefkochDomainModelCookbookCategory; use ChefkochApplicationCookbookNotificationCategorySavedSuccess; use ChefkochApplicationCookbookResponseCategoryResponse; use PixelhouseApplicationResponse; ! class SaveCategory implements UseCase { ... ! public function run(SaveCategoryCommand $command) { $user = $this->userRepository->findOneById($this->context->getUserId()); ! $category = new Category( $command->getCookbookId(), $user->getId(), $command->getCategoryRequest()->getName(), $command->getCategoryRequest()->getDescriptionText() ); ! $this->categoryRepository->add($category); ! $categoryResponse = new CategoryResponse($category, $user); ! $response = new Response(); $response->getNotification()->addMessage(new CategorySavedSuccess($categoryResponse)); ! return $response; } }
  • 25. HTTP/1.1 201 Created
  • 26. Per Bernhardt info@perprogramming.de perprogramming.de slideshare.net/perprogramming
  • 27. Bilder • Warning: https://www.flickr.com/photos/gerardstolk/ 6538330609 • Show Time: https://www.flickr.com/photos/ florida_photo_guy/5212663826 • Question Marks: https://www.flickr.com/photos/ loneblackrider/315302588 • Thank You: https://www.flickr.com/photos/wwworks/ 4759535950