Divide and Conquer

Divide and Conquer

E9612cd342dbddff6640b99db21deee7?s=128

Andreas Hucks

January 30, 2017
Tweet

Transcript

  1. 3.

    What this talk is not about: • DDD • Generic

    Package Design (but these topics will play a role)
  2. 5.
  3. 6.
  4. 7.
  5. 8.

    /**
 * @Route(path="/", name="list")
 */
 public function indexAction()
 {
 $robots

    = $this
 ->getDoctrine()
 ->getRepository('AppBundle:Robot')
 ->findAll()
 ;
 
 return $this->render(
 '@App/robot/index.html.twig',
 [
 'robots' => $robots
 ]
 );
 }
  6. 9.

    /**
 * @Route(path="/{id}", name="edit")
 */
 public function editAction(Request $request, Robot

    $robot)
 {
 $form = $this->createForm(RobotType::class, $robot);
 $form->handleRequest($request);
 
 if ($form->isSubmitted() && $form->isValid()) {
 $this->getDoctrine()->getManager()->flush();
 
 return $this->redirectToRoute('list');
 }
  7. 10.

    /**
 * @ORM\Entity(repositoryClass="…")
 * @ORM\Table(name="robots")
 */
 class Robot
 {
 public

    static $propusionTypes = [
 'legs' => 'Can walk',
 'wheels' => 'Drives',
 'tracks' => 'Tracked Vehicle',
 'jetpack' => 'Hovers',
 'weird' => 'Why would you build that'
 ];
 
 /**
 *@ORM\Column(type="integer")
 * @ORM\Id()
 * @ORM\GeneratedValue()
 */
 private $id;
  8. 11.

    public function validatePropulsion( ExecutionContextInterface $context, $payload ) {
 if (!in_array($this->getPropulsion(),

    self::$propusions)) { $context->buildViolation('Unknown propulsion type!')
 ->atPath('propulsion')
 ->addViolation();
 }
 }
  9. 12.

    namespace AppBundle\Entity;
 
 use Doctrine\ORM\Mapping as ORM;
 use Symfony\Component\Security\Core\User\UserInterface;
 use

    Symfony\Component\Validator\Constraints as Assert;
 
 /**
 * @ORM\Entity()
 * @ORM\Table(name="mechanics")
 */
 class Mechanic implements UserInterface
 {
 // …
  10. 14.
  11. 19.

    Clean Packages • Separate your business code from your framework

    • … or in fact, any other 3rd party libs • Separate your own layers/packages (Domain, Application, Infrastructure…)
  12. 21.
  13. 22.
  14. 23.
  15. 24.

    Domain\Entity\Robot:
 type: entity
 table: robots
 repositoryClass: Domain\Repository\RobotRepository
 id:
 id:
 type:

    uuid
 length: 255
 fields:
 name:
 type: string
 length: 255
 nullable: false
 […]
 embedded:
 propulsion:
 class: Domain\Entity\Data\Propulsion
 columnPrefix: false
  16. 25.

    public function __construct($value)
 {
 $propulsion = (string) $value;
 
 if

    (!in_array($propulsion, array_keys(self::$propusions))) {
 throw new Exception('Invalid key');
 }
 
 $this->propulsion = $propulsion;
 }
  17. 27.
  18. 28.

    class Id
 {
 private $key;
 
 public function __construct($key)
 {


    $this->key = (string) $key;
 }
 
 public function getKey()
 {
 return $this->key;
 }
  19. 30.

    
 class Uuid extends Type
 {
 public function convertToDatabaseValue($value, AbstractPlatform

    $platform)
 {
 if (empty($value)) {
 return null;
 }
 
 if ($value instanceof Id) {
 return $value->getKey();
 }
 
 return null;
 }
 
 
 
 public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $pl {
 return 'VARCHAR(255) COMMENT \'(DC2Type:uuid)\'';
 }
 
 public function getName()
 {
 return 'uuid';
 }
 }
  20. 31.
  21. 33.
  22. 34.
  23. 35.

    layers:
 - name: Application
 collectors:
 - type: className
 regex: ^AppBundle\\.*


    - name: Domain
 collectors:
 - type: className
 regex: ^Domain\\.*
 - name: Framework
 collectors:
 - type: className
 regex: ^Symfony\\|^Doctrine\\.*
 ruleset:
 Application:
 - Domain
 - Framework
 Application:
 - Domain
  24. 36.
  25. 37.
  26. 39.
  27. 40.
  28. 41.

    namespace Domain\Repository;
 
 use Domain\Entity\Data\Id;
 use Domain\Entity\Robot; interface RobotRepository
 {


    public function get(Id $id);
 
 public function add(Robot $robot); // […]
 }
  29. 42.

    namespace Bridge\Doctrine\Repository;
 
 use Doctrine\ORM\EntityRepository;
 use Domain\Entity\Data\Id;
 use Domain\Entity\Robot;
 use

    Domain\Repository\RobotRepository as RepositoryInterface; class RobotRepository extends EntityRepository implements Reposit {
 public function get(Id $id)
 {
 return $this->findOneBy(['id' => $id]);
 }
 
 public function add(Robot $robot)
 {
 $this->_em->persist($robot);
 $this->_em->flush($robot);
 }
 }
  30. 43.
  31. 44.
  32. 46.

    namespace AppBundle\Entity;
 
 use Doctrine\ORM\Mapping as ORM;
 use Symfony\Component\Security\Core\User\UserInterface;
 use

    Symfony\Component\Validator\Constraints as Assert;
 
 /**
 * @ORM\Entity()
 * @ORM\Table(name="mechanics")
 */
 class Mechanic implements UserInterface
 {
 // …
  33. 47.

    
 interface UserProviderInterface
 {
 public function loadUserByUsername($username);
 
 public function

    refreshUser(UserInterface $user);
 
 public function supportsClass($class);
 }

  34. 48.
  35. 49.

    class MechanicAccount implements UserInterface
 {
 private $mechanicId;
 private $username;
 private

    $password;
 
 public static function fromMechanic(Mechanic $mechanic)
 {
 $account = new static();
 
 $account->mechanicId = $mechanic->getId();
 $account->username = $mechanic->getEmail();
 $account->password = $mechanic->getPassword();
 
 return $account;
 }
  36. 50.

    public function loadUserByUsername($username)
 {
 $mechanic = $this->repository
 ->findOneBy(['email' => new

    Email($username)]);
 
 if (null === $mechanic) {
 throw new UsernameNotFoundException( 'User does not exist.' );
 }
 
 return MechanicAccount::fromMechanic($mechanic);
 }
  37. 51.