$30 off During Our Annual Pro Sale. View Details »

Task Based UIs

Task Based UIs

PHP Benelux 2015 - Why do we organise our screens as grids, as if they were database tables? And why do we make forms that manipulate their records? Our user interfaces are very often based on the underlying database structure, while adding minimal value to the user’s workflow. A good user interface should not focus on data, grids or forms, but on the task at hand. It should show relevant state in order to make informed decisions. It should also guide the user to perform specific tasks. We will cover how we can make these tasks explicit in the user interface, but also how they can drive a whole architecture. In the end, a good user interface helps users achieve their goals.

Stijn Vannieuwenhuyse

January 23, 2015
Tweet

More Decks by Stijn Vannieuwenhuyse

Other Decks in Programming

Transcript

  1. TASK BASED UIS

    View Slide

  2. STIJN VANNIEUWENHUYSE
    @STIJNVNH

    View Slide

  3. THIS TALK IS NOT
    ABOUT UI DESIGN

    View Slide

  4. THIS TALK IS
    ABOUT SOFTWARE DESIGN

    View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. CRUD
    CREATE READ UPDATE DELETE

    View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. $ git push origin master
    To https://github.com/stivni/doodis.git
    ! [rejected] master -> master (non-fast-forward)
    error: failed to push some refs to 'https://github.com/stivni/doodis.git'
    To prevent you from losing history, non-fast-forward updates were rejected
    Merge the remote changes (e.g. 'git pull') before pushing again. See the
    'Note about fast-forwards' section of 'git push --help' for details.

    View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. SOFTWARE
    SHOULD GUIDE ITS USERS
    THROUGH THE BUSINESS PROCESS

    View Slide

  18. SOFTWARE
    SHOULD DELIVER
    VALUE TO THE BUSINESS

    View Slide

  19. HOW?

    View Slide

  20. View Slide

  21. View Slide

  22. View Slide

  23. $customer->moveTo(
    new Address(
    $street, $number, $zip, $city, $country
    )
    );
    $order->pay(Money::EUR(100));
    $order->checkout();

    View Slide

  24. View Slide

  25. View Slide

  26. COMMAND
    PATTERN

    View Slide

  27. File a bug
    Acknowledge a bug
    Decline a bug
    Describe a bug
    Categorize a bug
    Close a bug

    View Slide

  28. FileBug
    AcknowledgeBug
    DeclineBug
    DescribeBug
    CategorizeBug
    CloseBug

    View Slide

  29. class FileBug implements Command
    {
    private $id;
    private $description;
    public function __construct($id, $description)
    {
    //…
    }
    public function getId()
    {
    return $this->id;
    }
    public function getDescription()
    {
    return $this->description;
    }
    }

    View Slide

  30. class FileBugCommandHandler implements CommandHandler
    {
    private $bugRepository;
    public function __construct(BugRepository $bugRepository)
    {
    //…
    }
    public function handle(FileBug $command)
    {
    $this->bugRepository->add(
    new Bug(
    $command->getId(),
    $command->getDescription()
    ;
    }
    }

    View Slide

  31. class SimpleCommandDispatcher implements CommandDispatcher
    {
    /** @var CommandHandler[] */
    private $commandHandlers;
    public function dispatch(Command $command)
    {
    $commandType = $this->getTypeFromCommand($command);
    $this->commandHandlers[$commandType]->handle($command);
    }
    public function registerHandler(CommandHandler $commandHandler)
    {
    $commandType = $this->getTypeFromCommandHandler($command);
    $this->commandHandlers[$commandClass] = $commandHandler;
    }
    //private function getTypeFromCommand(Command $command)
    //private function getTypeFromCommandHandler(CommandHandler $command)
    }

    View Slide

  32. {
    “commandName”: “\\NameSpaced\\FileBug”,
    “payload”: {
    “id”: “1”,
    “description”: “The software doesn’t work”
    }
    }

    View Slide

  33. class SimpleJsonCommandDeserializer implements CommandDeserializer
    {
    public function deserialize($data)
    {
    $reflectionClass = new ReflectionClass($data->commandName);
    return $reflectionClass->newInstanceArgs($data->payload);
    }
    }

    View Slide

  34. class CommandDispatcherController
    {
    // /api/command/dispatch
    public function deserialize($serializedCommand)
    {
    $command = $this->commandDeserializer->deserialize(
    $serializedCommand
    );
    $commandDispatcher->dispatch($command);
    return new Response(“OK”, 200);
    }
    }

    View Slide

  35. DOODIS
    SOON ON MY GITHUB

    View Slide

  36. BUT HOW
    DOES THIS AFFECT THE UI?

    View Slide

  37. View Slide

  38. View Slide

  39. View Slide

  40. View Slide

  41. SEPARATE
    READ MODELS

    View Slide

  42. [
    {
    “bugId”: “1”,
    “description”: “Checkout fails with order >= 100 EUR”
    “affectedFeature”: “CheckoutOrder”,
    “usageOfFeature”: “2233”
    },

    ]

    View Slide

  43. class BugStatistic implements JsonSerializable
    {
    private $bugId;
    private $description;
    private $affectedFeature;
    private $usageOfFeature;
    //public function __constructor(…)
    //public function jsonSerialize()
    }

    View Slide

  44. class BugStatisticsController
    {
    /** @var BugStatisticsRepository */
    private $bugStatisticsRepository;
    // /api/bugs/statistics/overview
    public function overview()
    {
    $statistics =
    $this->bugStatisticsRepository->findAll();
    return new Response(json_encode($statistics));
    }
    }

    View Slide

  45. class QueryingBugStatisticsRepository implements BugStatisticsRepository
    {
    public function findAll()
    {
    $sql = ‘SELECT * FROM bugs JOIN usage ON bug.feature = usage.feature’;
    return array_map(
    function($record) {
    return new BugStatistic(
    $record->id,
    $record->description,
    $record->feature,
    $record->usage
    );
    },
    $this->fetchAll($sql)
    );
    }
    }

    View Slide

  46. CQRS
    COMMAND QUERY RESPONSIBILITY SEGREGATION PRINCIPLE

    View Slide

  47. View Slide

  48. View Slide

  49. View Slide

  50. View Slide

  51. View Slide

  52. MY APPLICATION IS JUST
    CRUD

    View Slide

  53. View Slide

  54. PROCESSES ARE HARDCODED
    I WANT MORE
    FLEXIBILITY

    View Slide

  55. BUILDING GOOD SOFTWARE
    IS ABOUT MAKING THE
    RIGHT TRADEOFFS

    View Slide

  56. View Slide

  57. QUESTIONS?

    View Slide

  58. THANK YOU
    I’M STIJN VANNIEUWENHUYSE
    @STIJNVNH
    PLEASE LEAVE FEEDBACK
    JOIND.IN/13112

    View Slide