CodeFest 2019. Василий Чирвон (MobileUp) — Android. Clean Is Dead. Заблуждаетесь!

16b6c87229eaf58768d25ed7b2bbbf52?s=47 CodeFest
April 06, 2019

CodeFest 2019. Василий Чирвон (MobileUp) — Android. Clean Is Dead. Заблуждаетесь!

В мире быстрых изменений и хайпа стоит иногда остановиться и вспомнить старые добрые понятия. Такие как панк-рок и Clean Architecture. Давайте же окинем Clean свежим взглядом, и я объясню, почему эти архитектурные принципы все ещё актуальны. Мы вспомним их суть, разберем старые заблуждения и обдумаем новые идеи.

16b6c87229eaf58768d25ed7b2bbbf52?s=128

CodeFest

April 06, 2019
Tweet

Transcript

  1. 2.

    Кто этот панк? Василий Чирвон @Jeevuz Тимлид в MobileUp Android

    более 6 лет Cоавтор библиотеки Cicerone Соавтор библиотеки RxPM Автор статьи «Заблуждения Clean Architecture» (54 плюса, 125000+ просмотров, в 600+ закладках)
  2. 3.

    Кто этот панк? Василий Чирвон @Jeevuz Тимлид в MobileUp Android

    более 6 лет Cоавтор библиотеки Cicerone Соавтор библиотеки RxPM Автор статьи «Заблуждения Clean Architecture» (54 плюса, 125000+ просмотров, в 600+ закладках)
  3. 4.

    Книга Uncle Bob’а • Вышла в сентябре 2017 • В

    2018 появилась на русском • Рекомендую прочитать «Все архитектуры подчиняются одним и тем же правилам» Роберт Мартин
  4. 7.

    Почему я здесь? • Люди продолжают заблуждаться • Новое поколение

    разработчиков • Хайп вокруг новых “архитектур” «Архитектура отражает важные проектные решения по формированию системы, где важность определяется стоимостью изменений» Гради Буч
  5. 9.

    Что важнее? • правильная работа программы (выбор руководства) • архитектура

    (правильный выбор) Важнее архитектура. Если работающую программу сложно изменять, то при смене требований она может сломаться и стать бесполезной.
  6. 11.

    Откуда заблуждения? Все хотят купить архитектуру в Ikea. Чтобы получить

    инструкцию к ней. Но для архитектуры нет инструкции. Есть только принципы, правила, рекомендации.
  7. 12.

    Нет инструкции? Не ищите инструкцию, учитесь думать. Даже примеры кода

    от Uncle Bob’a сильно отличаются: • github.com/cleancoders/CleanCodeCaseStudy • github.com/BernardNotarianni/payroll-PPP-sample • blog.cleancoder.com/uncle-bob/2016/01/04/ALittleArchitecture.html
  8. 13.

    Хорошая архитектура • Не зависит от фреймворков • Тестируемая •

    Не зависит от UI, БД, и внешнего мира «Цель архитектуры программного обеспечения – уменьшить человеческие трудозатраты на создание и сопровождение системы» Роберт Мартин
  9. 14.

    А может нам это не нужно? Заблуждения: • Мы мобильное

    приложение • Мы просто клиент (списки показывать) • Незачем не зависеть от Android
  10. 15.

    А может нам это не нужно? Заблуждения: • Мы мобильное

    приложение • Мы просто клиент (списки показывать) • Незачем не зависеть от Android Нужно, потому что: • На мобиле бывает много логики. • При создании записи мы уже не клиент. • Мультиплатформа может выстрелить, а в SDK бывают баги. • Есть другие плюсы архитектуры.
  11. 17.

    Истоки Clean Architecture предшествовали: • Hexagonal Architecture (она же Ports

    and Adapters) • Onion Architecture • DCI (Data-Context-Interaction) • BCE (Boundary-Control-Entity)
  12. 18.

    Истоки Clean Architecture предшествовали: • Hexagonal Architecture (она же Ports

    and Adapters) • Onion Architecture • DCI (Data-Context-Interaction) • BCE (Boundary-Control-Entity) Эти архитектуры похожи: • Делят на уровни • Отделен уровень бизнес-правил • Обладают характеристиками хорошей архитектуры
  13. 19.

    Основано на принципах В основе Clean лежат принципы: • SOLID

    • Организации компонентов «Принципы SOLID определяют, как выкладывать кирпичами стены, образующие комнаты, а принципы организации компонентов – как размещать комнаты в зданиях» Роберт Мартин
  14. 22.

    Принципы SOLID • Принцип единственной ответственности (SRP) Разделяйте код, от

    которого зависят разные акторы. • Принцип открытости/закрытости (OCP) Делите систему на компоненты так, чтобы компоненты выше были защищены от изменений на нижних уровнях.
  15. 23.

    Принципы SOLID • Принцип единственной ответственности (SRP) Разделяйте код, от

    которого зависят разные акторы. • Принцип открытости/закрытости (OCP) Делите систему на компоненты так, чтобы компоненты выше были защищены от изменений на нижних уровнях. • Принцип подстановки Барбары Лисков (LSP) Реализации интерфейсов должны быть заменяемыми (совместимы).
  16. 24.

    Принципы SOLID • Принцип единственной ответственности (SRP) Разделяйте код, от

    которого зависят разные акторы. • Принцип открытости/закрытости (OCP) Делите систему на компоненты так, чтобы компоненты выше были защищены от изменений на нижних уровнях. • Принцип подстановки Барбары Лисков (LSP) Реализации интерфейсов должны быть заменяемыми (совместимы). • Принцип разделения интерфейсов (ISP) Не создавайте зависимости от модулей, содержащих больше, чем требуется.
  17. 25.

    Принципы SOLID • Принцип единственной ответственности (SRP) Разделяйте код, от

    которого зависят разные акторы. • Принцип открытости/закрытости (OCP) Делите систему на компоненты так, чтобы компоненты выше были защищены от изменений на нижних уровнях. • Принцип подстановки Барбары Лисков (LSP) Реализации интерфейсов должны быть заменяемыми (совместимы). • Принцип разделения интерфейсов (ISP) Не создавайте зависимости от модулей, содержащих больше, чем требуется. • Принцип инверсии зависимостей (DIP) Зависьте от стабильных абстракций вместо изменчивых конкретных элементов системы.
  18. 26.

    Организация компонентов Компоненты – единицы развертывания (jar, aar, модули). Принципы:

    • связность компонентов (какие классы отнести к какому компоненту) • сочетаемость компонентов (правила их взаимоотношений)
  19. 28.

    Связность компонентов • Принцип эквивалентности повторного использования и выпусков (REP)

    Составляйте компонент из классов, тесно связанных между собой.
  20. 29.

    Связность компонентов • Принцип эквивалентности повторного использования и выпусков (REP)

    Составляйте компонент из классов, тесно связанных между собой. • Принцип согласованного изменения (CCP) Собирайте в компонент то, что изменяется по одной причине и в одно время.
  21. 30.

    Связность компонентов • Принцип эквивалентности повторного использования и выпусков (REP)

    Составляйте компонент из классов, тесно связанных между собой. • Принцип согласованного изменения (CCP) Собирайте в компонент то, что изменяется по одной причине и в одно время. • Принцип совместного повторного использования (CRP) Не зависьте от компонентов, имеющих неиспользуемые классы.
  22. 32.

    Сочетаемость компонентов • Принцип ацикличности зависимостей (ADP) Разрывайте циклы в

    зависимостях компонентов (используя DIP или создав новый компонент от которого зависят другие).
  23. 33.

    Сочетаемость компонентов • Принцип ацикличности зависимостей (ADP) Разрывайте циклы в

    зависимостях компонентов (используя DIP или создав новый компонент от которого зависят другие). • Принцип устойчивых зависимостей (SDP) Зависьте от устойчивых. Трудно изменяемые компоненты не должны зависеть от изменчивых компонентов.
  24. 34.

    Сочетаемость компонентов • Принцип ацикличности зависимостей (ADP) Разрывайте циклы в

    зависимостях компонентов (используя DIP или создав новый компонент от которого зависят другие). • Принцип устойчивых зависимостей (SDP) Зависьте от устойчивых. Трудно изменяемые компоненты не должны зависеть от изменчивых компонентов. • Принцип устойчивости абстракций (SAP) Делайте устойчивые компоненты абстрактными, а неустойчивые конкретными.
  25. 36.

    Архитектура • Это форма системы. • Образуется делением на компоненты.

    • Определяет отношения между компонентами. • Упрощает разработку, развертывание и сопровождение. • Выделяет политики, отодвигая на задний план детали. «Хороший архитектор максимизирует количество непринятых решений» Роберт Мартин
  26. 37.

    Отделять надо всё, что изменяется по разным причинам и с

    разной скоростью: • Разделение по уровням. (UI, бизнес-правила, БД, прочее) • Разделение по вариантам использования. (пересекая уровни) Где провести границы нам подсказали SRP и ССP. Разделение
  27. 40.

    Круги Круги на схеме - это разные уровни программы. Ближе

    к центру - выше уровень. • Сущности (Бизнес-правила уровня предприятия). • Варианты использования (Бизнес-правила приложения). • Адаптеры интерфейсов (Presenter, Gateway). • Фреймворки и драйверы (UI, БД, Web).
  28. 42.

    Правило зависимостей Зависимости в исходном коде должны быть направлены внутрь,

    в сторону высокоуровневых политик. Внутренние круги ничего не знают о внешних: • Имена из внешних, не упоминаться в коде внутренних. • Форматы данных из внешних не используются внутренними.
  29. 43.

    Сущности • Содержат бизнес-правила уровня предприятия. • Могут использоваться разными

    приложениями. • Это объекты с методами. • В обособленном приложении выделяйте в сущности самые высокоуровневые правила. «Сущность - это бизнес в чистом виде и больше ничего» Роберт Мартин
  30. 44.

    Сущности Пример. Сущность (entity) для контакта телефонной книги. Бизнес-правила, общие

    для всех приложений: • Когда контакты считать одинаковыми. • Как объединять контакты. (Какое имя выбрать, если контакты совпали по номеру телефона. Какую дату и тп.) Contact -name: String -phones: List<String> -created: Date +same(contact: Contact) +combine(other: Contact)
  31. 45.

    Варианты использования • Бизнес-правила, характерные для приложения. • Имеют смысл

    только как часть приложения. • Используют сущности для достижения своих целей. • Это объект с методом, реализующим бизнес-правило. «Хорошие архитектуры опираются на варианты использования» Роберт Мартин
  32. 46.

    Варианты использования Пример. Объединить все похожие контакты. Ввод: нет Вывод:

    сколько лишних контактов объединилось Порядок действий: 1. Взять все контакты. 2. Найти среди них похожие. 3. Объединить. 4. Удалить объединенные с другими контакты. 5. Сохранить новый список контактов. 6. Отобразить количество удаленных лишних.
  33. 47.

    Варианты использования Заблуждение: Interactor и use case – разное. Это

    одно и то же. Interactor – объект, реализующий вариант использования (use case), используя бизнес-объекты (entities). Мы можем назвать наш вариант использования из примера как CombineContactsUseCase, так и CombineContactsInteractor. Это дело вкуса.
  34. 48.

    Варианты использования Заблуждение: Interactor это набор UseCase’ов. Нет. Вариант использования

    стоит делать отдельным объектом, не объединяя много вариантов в один класс. Объединяя много вариантов использования в один класс вы нарушите SRP и ISP. И лишитесь подробного описания вариантов использования в структуре проекта.
  35. 49.

    Адаптеры интерфейсов • Преобразуют данные из формата внутреннего в формат

    внешнего слоя. И наоборот. • На этом уровне презентационные паттерны. • На этом уровне Gateway/Repository.
  36. 50.

    Gateway/Repository «Классические» трактования этих паттернов вы найдете у Мартина Фаулера:

    Gateway – объект, который инкапсулирует доступ к внешней системе или ресурсу. martinfowler.com/eaaCatalog/gateway.html Repository – посредник между доменом и слоями преобразования данных, использующий для доступа к данным интерфейс, подобный коллекциям. martinfowler.com/eaaCatalog/repository.html
  37. 51.

    Gateway/Repository Распространено «новое» трактование: Gateway/Repository – объект, который инкапсулирует выбор

    различных источников данных. • Вариант использования не знает, откуда пришли данные. • Иногда часть логики «утекает» в Gateway/Repository.
  38. 52.

    Gateway Заблуждение: Удобно объединить Interactor и Gateway. Плохая идея. Вы

    нарушите SRP, ISP, OCP, DIP и правило зависимостей. Объединяя варианты использования вашего приложения со способами и источниками получения данных, вы делаете шаг назад от чистой архитектуры.
  39. 53.

    Фреймворки и драйверы • Содержит детали, которые не должны влиять

    на внутренние уровни. • Фреймворки и инструменты (UI, БД, Web). • В основном код для связи с внутренним уровнем адаптеров интерфейсов.
  40. 55.

    Общая картина IView <I> View Presenter Input Boundary <I> Output

    Boundary <I> Output Data <DS> Input Data <DS> Use Case Interactor IPresenter <I> Gateway <I> Gateway Impl Database Entity Entities
  41. 56.

    Почти круги IView <I> View Presenter Input Boundary <I> Output

    Boundary <I> Output Data <DS> Input Data <DS> Use Case Interactor IPresenter <I> Gateway <I> Gateway Impl Database Entity Entities
  42. 57.

    Границы IView <I> View Presenter Input Boundary <I> Output Boundary

    <I> Output Data <DS> Input Data <DS> Use Case Interactor IPresenter <I> Gateway <I> Gateway Impl Database Entity Entities
  43. 58.

    Границы Вариант использования не может вызвать Presenter напрямую. • Используем

    DIP чтобы развернуть зависимость. • Boundaries - пограничные интерфейсы, порты. • Входные и выходные Boundaries и данные находятся во внутреннем круге. Presenter Input Boundary <I> Output Boundary <I> Use Case Interactor Input Data <DS> Output Data <DS>
  44. 59.

    Эти схемы об одном и том же. Границы Presenter Input

    Boundary <I> Output Boundary <I> Output Data <DS> Input Data <DS> Use Case Interactor
  45. 60.

    Границы • Важная часть архитектуры. • Части системы свободны от

    лишних знаний друг о друге. • Помогают отложить преждевременные решения. «Разработка архитектуры программного обеспечения – это искусство проведения разделяющих линий, которые я называю границами» Роберт Мартин
  46. 61.

    Что пересекает границы • Простые структуры или объекты передачи данных

    (DTO). • Способ передачи не важен. • Главное чтобы простые, независимые структуры данных. • Всегда в форме более удобной для внутреннего круга. Рекомендации от Дядюшки Боба: • Не передавайте объекты сущностей или записи БД. • И не включайте ссылки на сущности в DTO.
  47. 62.

    Как это работает IView <I> View Presenter Input Boundary <I>

    Output Boundary <I> Output Data <DS> Input Data <DS> Use Case Interactor IPresenter <I> Gateway <I> Gateway Impl Database Entity Entities
  48. 64.

    У меня Rx, где Boundary? Если вы используете реактивные потоки

    (например, RxJava), то у вас получится такая схема. Publisher и Subscriber логически формируют Output Boundary. Presenter Input Boundary <I> Publisher <I> Subscriber <I> Output Data <DS> Input Data <DS> Use Case Interactor Output Boundary
  49. 65.

    Controllers В статье я писал: «В android-приложениях Controllers не нужны»

    Мысли: Иногда бывают кейсы, когда надо слушать сервер и реагировать как на ввод, переключая экраны или запуская UseCase. Это очень похоже на обязанности Controller в веб- приложениях. Мы могли бы использовать подобные сущности в мобильных приложениях для таких кейсов..
  50. 66.

    В Gateway через Interactor В статье я писал: «не вижу

    ничего страшного, чтобы … вызывать Gateway из Presenter’a, минуя Interactor» Я заблуждался: Симон Браун называет это «окружным антишаблоном портов и адаптеров». Это потенциальный компромисс, который может иметь плохие последствия.
  51. 68.

    Clean vs. all Часто хайп вокруг чего-то нового заставляет нас

    сомневаться в актуальности Сlean Architecture. • MVP (хайп в 2015) • MVVM (хайп в 2016 и в 2018) • RxPM (не было хайпа, просто рекламирую) • VIPER (iOs больше, в 2015 хайп вроде был) • MVI (хайп в 2017 и сейчас)
  52. 69.

    Clean vs. all Заблуждение: MVP, MVVM, PM или Clean? Так

    сравнивать нельзя. Всё перечисленное – это шаблоны проектирования пользовательского интерфейса. Нельзя вот так сравнивать их с Clean. Эти шаблоны являются частью чистой архитектуры и расположены в слое адаптеров интерфейсов.
  53. 70.

    Clean vs. all Заблуждение: VIPER заменяет Clean. Нет. VIPER –

    набор классов из описания Сlean Architecture, который преподносится как реализация чистой архитектуры. Набор классов с нужными именами это еще не архитектура.
  54. 71.

    Clean and MVI Многие говорят, что MVI в Android –

    это чисто для UI. Мне же кажется, что MVI вполне можно использовать шире, организовав так, чтобы удовлетворять принципам чистой архитектуры. Но это не точно. А что думаете вы?
  55. 73.

    Clean Not Dead Clean Architecture даёт нам принципы, правила и

    рекомендации, следуя которым мы упрощаем создание и сопровождение наших систем. «Правила не изменились. Несмотря на появление новых языков, фреймворков и парадигм, правила остались теми же, как во времена, когда в 1946-м Алан Тьюринг написал первый программный код» Роберт Мартин
  56. 74.

    Спасибо. Василий Чирвон, @Jeevuz, vasili@mobileup.ru Статья: habr.com/ru/company/mobileup/blog/335382/ RxPM: github.com/dmdevgo/RxPM Cicerone:

    github.com/terrakok/Cicerone «And remember, we are all pirates by nature; and the rules I'm talking about here are really more like guidelines…» Uncle Bob