Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Иван Матвеев. Как услышать бизнес и сделать быстро, в бюджет и качественно: уходим от feature-based разработки и концентрируемся на домене

Иван Матвеев. Как услышать бизнес и сделать быстро, в бюджет и качественно: уходим от feature-based разработки и концентрируемся на домене

Мы пишем код за деньги и решаем проблемы бизнеса, а значит бизнес-логика — важнейшая часть наших систем. Domain driven design (DDD) как раз об этом. Но часто DDD представляют как набор паттернов: aggregate root, repositories, ubiquitous language… Однако, чтобы строить приложение и делать хорошо для команды и бизнеса, необязательно все это применять. Я расскажу:
* о комбинации архитектурных практик, которые мы внедряем в Skyeng — и пути проб и ошибок, которым пришли в ним
* как мы определяем, что важно для бизнеса, и как гексагональная архитектура позволяет нам концентрироваться на домене
* и как мы постепенно переходим от feature based разработки к настоящему domain driven

Python Community Chelyabinsk

October 07, 2019
Tweet

More Decks by Python Community Chelyabinsk

Other Decks in Programming

Transcript

  1. Переходим
    от Feature-based разработки
    к Domain Driven Design

    View Slide

  2. SkyEng
    ✤ Skyeng — это онлайн-школа английского языка нового поколения.
    ✤ В школе работают профессионалы, помогающие жителям
    современных мегаполисов выучить английский язык в условиях
    недостатка времени.

    View Slide

  3. View Slide

  4. Маркетинг
    ✤ Ма
    ́ рке
    ́ тинг (от англ. marketing «рыночная деятельность») —
    организационная функция и совокупность процессов создания,
    продвижения и предоставления продукта или услуги покупателям
    и управление взаимоотношениями с ними с выгодой для организации.

    View Slide

  5. Маркетинг
    ✤ Тратить меньше (на продвижение и предоставление)
    ✤ Получать больше (увеличение аудитории, создание новых продуктов)
    ✤ Деньги!

    View Slide

  6. View Slide

  7. Что требуется от разработки
    ✤ Качественно
    ✤ Быстро
    ✤ Дешево

    View Slide

  8. Пример

    View Slide

  9. Задачка
    ✤ Надо сделать виджет
    ✤ Дизайн есть!
    ✤ Пользователь оставляет заявку
    ✤ В календаре выбирает дату и время вводного урока

    View Slide

  10. Вводный урок
    Что это?

    View Slide

  11. Задачка
    ✤ Надо сделать виджет
    ✤ Дизайн есть!
    ✤ Пользователь оставляет заявку
    ✤ В календаре выбирает дату и время вводного урока

    View Slide

  12. View Slide

  13. Как будем делать тех. ревью?
    ✤ От базы/интеграций
    ✤ От api фронтенда
    ✤ От проблемы бизнеса

    View Slide

  14. И от базы/интеграций и от api
    ✤ Сервис букинга — получить, выбрать, отменить
    ✤ Фронтовое api — получить, выбрать, отменить
    ✤ База — таблица свзяка education_service_id, booking_slot_id
    ✤ Что там дальше, подумаем потом ;)
    ✤ Что думает по этому поводу бизнес?

    View Slide

  15. View Slide

  16. View Slide

  17. Чего хочет бизнес от разработки.
    ✤ Качественно — не терять сценарии и лучше понимать бизнес
    проблемы
    ✤ Быстро — быстро описывать сценарии в коде в отрыве от конкретных
    технологий (технологии не важны)
    ✤ Дешево — возможность проверять сценарии как можно раньше (оно
    вообще работает? может и разрабатывать не надо?)

    View Slide

  18. Что дальше?
    ✤ Опишем сервис
    ✤ Пробуем удовлетворить все 3 потребности
    ✤ Посмотрим со стороны гексагональной архитектуры

    View Slide

  19. «Allow an application to equally be driven by users, programs, automated test or
    batch scripts, and to be developed and tested in isolation from its eventual run-
    time devices and databases.».
    — Alistair Cockburn
    Позволяет взаимодействовать с приложением как пользователю, так и
    программам, автоматическим тестам, скриптам пакетной обработки.
    Также позволяет разрабатывать и тестировать приложение без каких-либо
    дополнительных устройств или баз данных.

    View Slide

  20. Hexagonal architecture / Ports and adapters
    Пользователь

    Программы

    Тесты

    Скрипты

    Базы

    Внешние Апи

    Доп. устройства

    Приложение

    View Slide

  21. Попробуем описать сервис — Application
    Приложение

    View Slide

  22. ✤ Какие сервисы нужны? 

    (что если их нет? или есть?)
    ✤ Что нам понадобится от
    них?
    ✤ Что нужно от репозитория?

    View Slide

  23. class SelfTrialBookingService
    {
    ...
    public function __construct(
    OperatorsServiceInterface $operatorsService,
    BookingServiceInterface $bookingService,
    SelfTrialRepositoryInterface $selfTrialRepository
    )
    {
    $this->operatorsService = $operatorsService;
    $this->bookingService = $bookingService;
    $this->selfTrialRepository = $selfTrialRepository;
    }
    }
    ✤ Какие сервисы нужны? 

    (что если их нет? или есть?)
    ✤ Что нам понадобится от
    них?
    ✤ Что нужно от репозитория?

    View Slide

  24. interface OperatorsServiceInterface
    {
    public function holdCall(int $educationServiceId, DateInterval $interval, string $reason): void;
    public function disableCall(int $educationServiceId, string $reason): void;
    public function enableCall(int $educationServiceId, string $reason): void;
    }
    ✤ Отложить звонок, сразу после заявки
    ✤ Отменить звонок вообще, если пользователь выбрал дату/время
    ✤ Назначить звонок, если пользователь передумал

    View Slide

  25. interface BookingServiceInterface
    {
    public function bookSlot(string $slotId, string $reason): void;
    public function cancelSlot(string $slotId, string $reason): void;
    public function getAvailableSlots();
    }
    ✤ Выбрать дату/время
    ✤ Отменить дату/время
    ✤ Получить список дат/времени

    View Slide

  26. interface SelfTrialRepositoryInterface
    {
    public function save(SelfTrial $selfTrial): void;
    public function getSelfTrialByEducationServiceId(int $educationServiceId): ?SelfTrial;
    }
    ✤ Сохранить
    ✤ Получить

    View Slide

  27. Начинаем процесс подбора

    View Slide

  28. public function startSelfTrialProcess(int $educationServiceId): void
    {
    $this->operatorsService->holdCall(
    $educationServiceId,
    new DateInterval(self::HOLD_CALL_INTERVAL),
    'self_trial'
    );
    $selfTrial = SelfTrial::start($educationServiceId);
    $this->selfTrialRepository->save($selfTrial);
    }
    public static function start(int $educationServiceId) : SelfTrial
    {
    $instance = new self($educationServiceId);
    $instance->status = SelfTrialStatus::STARTED();
    return $instance;
    }

    View Slide

  29. View Slide

  30. Выбираем слот

    View Slide

  31. public function bookSlot(int $educationServiceId, string $slotId, string $reason): void
    {
    $selfTrial = $this
    ->selfTrialRepository
    ->getSelfTrialByEducationServiceId($educationServiceId);
    //Валидация и DomainException
    $this->bookingService->bookSlot($slotId, $reason);
    $this->operatorsService->disableCall($educationServiceId, $reason);
    $selfTrial->bookSlot($slotId);
    $this->selfTrialRepository->save($selfTrial);
    }
    public function bookSlot(string $slotId)
    {
    $this->slotId = $slotId;
    $this->status = SelfTrialStatus::BOOKED();
    }

    View Slide

  32. View Slide

  33. Отменяем слот

    View Slide

  34. public function cancelSelfTrial(int $educationServiceId, string $reason): void
    {
    $selfTrial = $this
    ->selfTrialRepository
    ->getSelfTrialByEducationServiceId($educationServiceId);
    //Валидация и DomainException
    $this->bookingService->cancelSlot($selfTrial->getSlotId(), $reason);
    $this->operatorsService->enableCall($educationServiceId, $reason);
    $selfTrial->cancel();
    $this->selfTrialRepository->save($selfTrial);
    }
    public function cancel()
    {
    $this->status = SelfTrialStatus::CANCELLED();
    }

    View Slide

  35. Да

    View Slide

  36. Application
    ✤ Качественно — не терять сценарии и лучше понимать бизнес
    проблемы
    ✤ Быстро — быстро описывать сценарии в коде в отрыве от конкретных
    технологий (технологии не важны)
    ✤ Дешево — возможность проверять сценарии как можно раньше (оно
    вообще работает? может и разрабатывать не надо?)

    View Slide

  37. Насколько это гибко?
    Вот тут будет кнопка отмены!!!

    View Slide

  38. Да

    View Slide

  39. Насколько это гибко?

    View Slide

  40. public function bookSlot(int $educationServiceId, string $slotId, string $reason): void
    {
    $selfTrial = $this
    ->selfTrialRepository
    ->getSelfTrialByEducationServiceId($educationServiceId);
    //Валидация и DomainException
    $this->bookingService->bookSlot($slotId, $reason);
    $this->operatorsService->disableCall($educationServiceId, $reason);
    $this->operatorsService->enableCall($educationServiceId, $reason);
    $selfTrial->bookSlot($slotId);
    $this->selfTrialRepository->save($selfTrial);
    }

    View Slide

  41. + обновление деталей звонка
    Да

    View Slide

  42. При чем тут DDD?
    ✤ Разработка от предметной области
    ✤ Ubiquitous Language (единый язык)
    ✤ Можно показать заказчику
    ✤ Если не поймет, покрыть BDD тестом
    ✤ Bounded Context (контекст предметной области)

    View Slide

  43. Hexagonal architecture / Ports and adapters
    Пользователь

    Программы

    Тесты

    Скрипты

    Базы

    Внешние Апи

    Доп. устройства

    Приложение

    View Slide

  44. Hexagonal architecture / Ports and adapters
    Приложение

    View Slide

  45. Primary adapters
    Пользователь

    Программы

    Тесты

    Скрипты


    View Slide

  46. $bookingService = new SelfTrialBookingService(
    new OperatorsServiceStupidMock(),
    new BookingServiceStupidMock(),
    new SelfTrialRepositoryStupidMock()
    );
    $bookingService->setLogger(new \Psr\Log\NullLogger());
    echo 'Starting self trial' . PHP_EOL;
    $bookingService->startSelfTrialProcess(1);
    echo '[:::::::::::::::::::::::::]' . PHP_EOL;
    echo 'Book slot' . PHP_EOL;
    $bookingService->bookSlot(1, 'slot-1', 'api_call_from_user_book');
    echo '[:::::::::::::::::::::::::]' . PHP_EOL;
    echo 'Cancel slot' . PHP_EOL;
    $bookingService->cancelSelfTrial(2, 'api_call_from_user_cancel');

    View Slide

  47. Secondary adapters
    Базы

    Внешние Апи

    Доп. устройства


    View Slide

  48. Starting self trial
    OperatorsServiceStupidMock: Call hold for 1 due to self_trial
    SelfTrialRepositoryStupidMock: Saved SelfTrial for 1
    [:::::::::::::::::::::::::]
    Book slot
    SelfTrialRepositoryStupidMock: Found SelfTrial for 1
    BookingServiceStupidMock: Slot slot-1 booked for 1 due to api_call_from_user_book
    OperatorsServiceStupidMock: Call disabled for 1 due to api_call_from_user_book
    SelfTrialRepositoryStupidMock: Saved SelfTrial for 1
    [:::::::::::::::::::::::::]
    Cancel slot
    SelfTrialRepositoryStupidMock: Found SelfTrial for 2
    BookingServiceStupidMock: Slot slot-2 cancelled due to api_call_from_user_cancel
    OperatorsServiceStupidMock: Call enabled for 2 due to api_call_from_user_cancel
    SelfTrialRepositoryStupidMock: Saved SelfTrial for 1

    View Slide

  49. Более красивая картинка ;)
    ✤https://herbertograca.com/2017/09/14/ports-adapters-architecture/

    View Slide

  50. Зачем
    ✤ Позволяет концентрироваться на домене
    ✤ Выделение бизнес логики
    ✤ Тесты
    ✤ Заменяемые элементы
    ✤ Дисциплина (именование папок, куда что положить)

    View Slide

  51. Заметки на полях
    ✤ Что с атомарностю (@synchronized)?
    ✤ Почему не Symfony Workflow
    ✤ Рисуем Сову?
    ✤ Почему не ивенты из модели?
    ✤ Где эксепшены?
    ✤ Почему так много кода?

    View Slide

  52. Ссылки.
    ✤ https://www.infoq.com/minibooks/domain-driven-design-quickly/
    ✤ http://www.ntcoding.co.uk/workshops/strategic-ddd-practices
    ✤ http://www.ouarzy.com/2016/07/25/micro-service-and-bounded-context-
    clarification/
    ✤ http://www.dossier-andreas.net/software_architecture/
    ports_and_adapters.html
    ✤ https://github.com/Redjik/self-trial-hexagon

    View Slide

  53. Спасибо.
    ✤ https://www.infoq.com/minibooks/domain-driven-design-quickly/
    ✤ http://www.ntcoding.co.uk/workshops/strategic-ddd-practices
    ✤ http://www.ouarzy.com/2016/07/25/micro-service-and-bounded-context-
    clarification/
    ✤ http://www.dossier-andreas.net/software_architecture/
    ports_and_adapters.html
    ✤ https://github.com/Redjik/self-trial-hexagon

    View Slide