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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  6. Пример

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  14. «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 full-size slide

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

    Программы

    Тесты

    Скрипты

    Базы

    Внешние Апи

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

    Приложение

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

  19. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  23. 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 full-size slide

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

    View full-size slide

  25. 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 full-size slide

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

    View full-size slide

  27. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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);
    $this->operatorsService->enableCall($educationServiceId, $reason);
    $selfTrial->bookSlot($slotId);
    $this->selfTrialRepository->save($selfTrial);
    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    Программы

    Тесты

    Скрипты

    Базы

    Внешние Апи

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

    Приложение

    View full-size slide

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

    View full-size slide

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

    Программы

    Тесты

    Скрипты


    View full-size slide

  37. $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 full-size slide

  38. Secondary adapters
    Базы

    Внешние Апи

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


    View full-size slide

  39. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  43. Ссылки.
    ✤ 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 full-size slide

  44. Спасибо.
    ✤ 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 full-size slide