More Related Content
Similar to Application Layer in PHP (20)
More from Per Bernhardt (12)
Application Layer in PHP
- 4. Credit goes to…
• http://martinfowler.com/
• http://domainlanguage.com/
• http://alistair.cockburn.us/
• http://www.whitewashing.de/
• und viele andere..
- 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. 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
- …
- 16. <?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');
}
}
- 17. <?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;
}
}
- 18. <?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)
{
...
}
}
- 20. <?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;
}
}
}
}
- 21. <?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();
}
}
}
- 22. <?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());
}
}
}
- 23. <?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();
}
}
}
- 25. <?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)
{
...
}
}
- 26. <?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;
}
}