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

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

CodeFest
April 06, 2019

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

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

CodeFest

April 06, 2019
Tweet

More Decks by CodeFest

Other Decks in Technology

Transcript

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

    более 6 лет Cоавтор библиотеки Cicerone Соавтор библиотеки RxPM Автор статьи «Заблуждения Clean Architecture» (54 плюса, 125000+ просмотров, в 600+ закладках)
  2. Кто этот панк? Василий Чирвон @Jeevuz Тимлид в MobileUp Android

    более 6 лет Cоавтор библиотеки Cicerone Соавтор библиотеки RxPM Автор статьи «Заблуждения Clean Architecture» (54 плюса, 125000+ просмотров, в 600+ закладках)
  3. Книга Uncle Bob’а • Вышла в сентябре 2017 • В

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

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

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

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

    от Uncle Bob’a сильно отличаются: • github.com/cleancoders/CleanCodeCaseStudy • github.com/BernardNotarianni/payroll-PPP-sample • blog.cleancoder.com/uncle-bob/2016/01/04/ALittleArchitecture.html
  8. Хорошая архитектура • Не зависит от фреймворков • Тестируемая •

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

    приложение • Мы просто клиент (списки показывать) • Незачем не зависеть от Android
  10. А может нам это не нужно? Заблуждения: • Мы мобильное

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

    and Adapters) • Onion Architecture • DCI (Data-Context-Interaction) • BCE (Boundary-Control-Entity)
  12. Истоки Clean Architecture предшествовали: • Hexagonal Architecture (она же Ports

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

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

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

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

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

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

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

    Составляйте компонент из классов, тесно связанных между собой.
  20. Связность компонентов • Принцип эквивалентности повторного использования и выпусков (REP)

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

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

    зависимостях компонентов (используя DIP или создав новый компонент от которого зависят другие).
  23. Сочетаемость компонентов • Принцип ацикличности зависимостей (ADP) Разрывайте циклы в

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

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

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

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

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

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

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

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

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

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

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

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

    внешнего слоя. И наоборот. • На этом уровне презентационные паттерны. • На этом уровне Gateway/Repository.
  36. Gateway/Repository «Классические» трактования этих паттернов вы найдете у Мартина Фаулера:

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

    различных источников данных. • Вариант использования не знает, откуда пришли данные. • Иногда часть логики «утекает» в Gateway/Repository.
  38. Gateway Заблуждение: Удобно объединить Interactor и Gateway. Плохая идея. Вы

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

    на внутренние уровни. • Фреймворки и инструменты (UI, БД, Web). • В основном код для связи с внутренним уровнем адаптеров интерфейсов.
  40. Общая картина 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. Почти круги 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. Границы 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. Границы Вариант использования не может вызвать Presenter напрямую. • Используем

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

    Boundary <I> Output Boundary <I> Output Data <DS> Input Data <DS> Use Case Interactor
  45. Границы • Важная часть архитектуры. • Части системы свободны от

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

    (DTO). • Способ передачи не важен. • Главное чтобы простые, независимые структуры данных. • Всегда в форме более удобной для внутреннего круга. Рекомендации от Дядюшки Боба: • Не передавайте объекты сущностей или записи БД. • И не включайте ссылки на сущности в DTO.
  47. Как это работает 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. У меня 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. Controllers В статье я писал: «В android-приложениях Controllers не нужны»

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

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

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

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

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

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

    рекомендации, следуя которым мы упрощаем создание и сопровождение наших систем. «Правила не изменились. Несмотря на появление новых языков, фреймворков и парадигм, правила остались теми же, как во времена, когда в 1946-м Алан Тьюринг написал первый программный код» Роберт Мартин
  56. Спасибо. Василий Чирвон, @Jeevuz, [email protected] Статья: 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