application layer in php

32
Application Layer ein Vortrag von Per Bernhardt

Upload: per-bernhardt

Post on 06-May-2015

855 views

Category:

Technology


2 download

DESCRIPTION

Vortrag über eine beispielhafte Implementierung eines Application Layers in PHP

TRANSCRIPT

Page 1: Application Layer in PHP

Application Layerein Vortrag von Per Bernhardt

Page 2: Application Layer in PHP

Agenda

• Disclaimer / Credits

• Ein kleines bisschen Theorie

• Ein echtes Beispiel: Chefkoch API

Page 3: Application Layer in PHP
Page 4: Application Layer in PHP

Credit goes to…• http://martinfowler.com/

• http://domainlanguage.com/

• http://alistair.cockburn.us/

• http://www.whitewashing.de/

• und viele andere..

Page 5: Application Layer in PHP

Ein bisschen Theorie…

Page 6: Application Layer in PHP

Application Layer

Presentation Layer

Infrastructure Layer

Domain Layer

Page 7: Application Layer in PHP

Application Layer

Presentation Layer

Infrastructure Layer

Domain Layer

- Controller - Template / HTML - Session / HTTP

Page 8: Application Layer in PHP

Application Layer

Presentation Layer

Infrastructure Layer

Domain Layer

- Controller - Template / HTML - Session / HTTP

- Fasade - Transaktionen - Sicherheit / Zugriffsschutz - Integration / Orchestrierung

Page 9: Application Layer in PHP

Application Layer

Presentation Layer

Infrastructure Layer

Domain Layer

- Controller - Template / HTML - Session / HTTP

- Fasade - Transaktionen - Sicherheit / Zugriffsschutz - Integration / Orchestrierung

- Daten - Geschäftslogik

Page 10: Application Layer in PHP

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 - …

Page 11: Application Layer in PHP

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 - …

Page 12: Application Layer in PHP

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 - …

Page 13: Application Layer in PHP

Chefkoch API

Page 14: Application Layer in PHP
Page 15: Application Layer in PHP

POST /cookbooks/{id}/categories

Page 16: Application Layer in PHP

<?php !namespace Chefkoch\Bundle\ApiBundle\Controller; !use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Chefkoch\Domain\Model\Cookbook\CookbookId use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Chefkoch\Bundle\ApiBundle\Annotation\ApiSerialize; use Chefkoch\Application\Cookbook\Request; use Chefkoch\Application\Cookbook\Command; use Pixelhouse\Application\Event\DispatchingService; use Pixelhouse\Application\Response; !class CookbookController extends Controller { ! ... ! /** * @ParamConverter("category", options={"deserialize"=true}) * @ApiSerialize * @return Response */ public function saveCategoryAction(CookbookId $cookbookId, Request\CategoryRequest $category) { return $this->getCookbookService()->execute( new Command\SaveCategoryCommand($cookbookId, $category) ); } ! ... ! /** * @return DispatchingService */ private function getCookbookService() { return $this->get('chefkoch_api.application.cookbook_service'); } }

Page 17: Application Layer in PHP

<?php !namespace Chefkoch\Application\Cookbook\Command; !use Chefkoch\Application\Cookbook\Request\CategoryRequest; use Chefkoch\Application\Cookbook\Security\CookbookWriteAccessRequired; use Chefkoch\Domain\Model\Cookbook\CookbookId; use Pixelhouse\Application\Command; !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; } }

Page 18: Application Layer in PHP

<?php !namespace Pixelhouse\Application\Event; !use Pixelhouse\Application\Command; use Pixelhouse\Application\UseCase; use Pixelhouse\EventDispatcher\EventDispatcher; !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) { ... } }

Page 19: Application Layer in PHP

<?php !namespace Pixelhouse\Application\Event; !class Events { const PRE_COMMAND = 'application.pre_command'; const POST_COMMAND = 'application.post_command'; const EXCEPTION = 'application.exception'; }

Page 20: Application Layer in PHP

<?php !namespace Pixelhouse\Application\Event; !use Pixelhouse\Application\Command; !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; } } } }

Page 21: Application Layer in PHP

<?php !namespace Chefkoch\Infrastructure\Application; !use Pixelhouse\Application\Event; use Pixelhouse\EventDispatcher\Subscriber; use Doctrine\ORM\EntityManager; !class DoctrineTransactionListener implements Subscriber { ! /** @var EntityManager */ private $entityManager; ! public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; } ! public function preCommand(Event\CommandEvent $event) { $this->entityManager->getConnection()->beginTransaction(); } ! public function postCommand(Event\PostCommandEvent $event) { $this->entityManager->flush(); $this->entityManager->getConnection()->commit(); } ! public function onException(Event\CommandExceptionEvent $event) { $this->entityManager->close(); if ($this->entityManager->getConnection()->isTransactionActive()) { $this->entityManager->getConnection()->rollBack(); } } }

Page 22: Application Layer in PHP

<?php !namespace Pixelhouse\Application\Security; !use Pixelhouse\Application\Event; use Pixelhouse\EventDispatcher\Subscriber; !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(Event\CommandEvent $event) { foreach ($this->policies as $policy) { $policy->check($this->securityContext, $event->getCommand()); } } }

Page 23: Application Layer in PHP

<?php !namespace Chefkoch\Application\Cookbook\Security; !use Chefkoch\Domain\Model\Cookbook\CookbookRepository; use Chefkoch\User\Domain\Model\UserRepository; use Pixelhouse\Application\Command; use Pixelhouse\Application\Security\AccessDeniedException; use Pixelhouse\Application\Security\Context; use Pixelhouse\Application\Security\Policy; !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(); } } }

Page 24: Application Layer in PHP

<?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="Chefkoch\Application\Cookbook\UseCase\SaveCategory"> <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="Chefkoch\Application\Cookbook\Command\SaveCategoryCommand" applicationService="chefkoch_api.application.cookbook_service" /> </service> ! ... </services> </container>

Page 25: Application Layer in PHP

<?php !namespace Chefkoch\Application\Cookbook\UseCase; !use Chefkoch\Application\Cookbook\Command\SaveCategoryCommand; use Pixelhouse\Application\Security\Context; use Pixelhouse\Application\UseCase; use Chefkoch\Domain\Model\Cookbook\CategoryRepository; use Chefkoch\User\Domain\Model\UserRepository; !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) { ... } }

Page 26: Application Layer in PHP

<?php !namespace Chefkoch\Application\Cookbook\UseCase; !use Pixelhouse\Application\UseCase; use Chefkoch\Application\Cookbook\Command\SaveCategoryCommand; use Chefkoch\Domain\Model\Cookbook\Category; use Chefkoch\Application\Cookbook\Notification\CategorySavedSuccess; use Chefkoch\Application\Cookbook\Response\CategoryResponse; use Pixelhouse\Application\Response; !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; } }

Page 27: Application Layer in PHP

HTTP/1.1 201 Created

Page 28: Application Layer in PHP
Page 29: Application Layer in PHP
Page 30: Application Layer in PHP
Page 31: Application Layer in PHP

Per Bernhardt [email protected]

perprogramming.de slideshare.net/perprogramming

Page 32: Application Layer in PHP

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