Большие проекты, архитектура и фреймворки

Александр Макаров

Yii core team

http://slides.rmcreative.ru/2020/big-projects/

О себе

  • Долго занимаюсь Yii и другим открытым кодом.
  • Параллельно работал над коммерческими проектами.
  • Участвую в PHP-FIG.
  • Автор нескольких книг и rmcreative.ru.
  • Делаю PHP Russia.

Архитектура?

Чёткого определения нет :)

Ряд решений о том, как взаимодействуют отдельные части системы (в том числе люди). Это, в основном, глобальные решения, которые тяжело изменить потом.

  • На какие элементы разбить код?
  • Как эти элементы взаимодействуют?
  • Кто и за что отвечает.

Архитектура — это про соглашения, интерфейсы, контракты и абстракцию. Не про код и конкретную реализацию.

Что такое интерфейс?

Наследование знают все. А что такое инкапсуляция и полиморфизм? Какой их смысл?

Фреймворк — не архитектура

Он не построит архитектуру за вас.

Фреймворк ничего не знает об элементах вашего приложения и не может определить интерфейсы для взаимодействия этих элементов.

Но может дать начальный шаблон и инфраструктуру. В случае Yii это MVC.

basic/advanced — не догма и не шаблон полноценной архитектуры

Кстати, про MVC...

Controller

  • Принимает данные извне (GET, POST, консольный ввод, ...).
  • Отдаёт данные в нужном виде в Model и View.
  • Не реализует логику, не занимается форматированием или формированием ответа.

View

  • Получает подготовленные контроллером данные.
  • Форматирует данные для ответа.
  • Никогда не работает с внешними данными, базой или пользовательским вводом напрямую.

Model

  • Не Model в Yii! Не ActiveRecord!
  • M в MVC - не один класс, а целый доменный слой (вся логика приложения).
  • Получает подготовленные контроллером данные, обрабатывает их, возвращает результат.
  • Никогда не работает с внешними данными или пользовательским вводом напрямую.
  • Не занимается форматированием или формированием ответа.

ADR

Паттерны проектирования — не архитектура.

Это проверенные варианты решения более-менее распространённых проблем. У каждого паттерна есть как плюсы, так и минусы.

Даже правильно релизованные паттерны легко использовать неправильно.

Зачем нужна архитектура?

Для борьбы со сложностью.

Цель — сделать понятным каждый уровень абстракции.

  • Для изменения под реалии бизнеса.
  • Чтобы не приходилось всё переписывать с нуля.
  • Сделать с первого раза хорошо — это не просто.

Хорошая архитектура — это дорого. Плохая — еще дороже.

"If you think good architecture is expensive, try bad architecture"
– Brian Foote and Joseph Yoder

Паттерны — хорошо. Принципы — лучше.

SOLID

Ещё одна модная аббревиатура...

Single Responsibility

Класс должен делать что-то одно.

Single Responsibility

У класса должна быть одна причина для изменения.

Single Responsibility

Модуль должен служить одному типу потребителей.

Open-closed

Класс или модуль (несколько связанных классов) должен скрывать детали реализации (то есть как именно он работает внутри), но иметь чётко определённый интерфейс, который позволяет как использовать модуль или класс (public методы), так и расширять его наследованием (protected и public методы).

Liskov substitution

Принцип об иерархии и наследования. Классическое определение очень запутанное. Можно проще.

Когда мы реализовываем новый класс, наследуясь от существующего, новый должен быть с тем же интерфейсом и вести себя абсолютно так же в тех же ситуациях. Так, чтобы программа работала, если подсунуть ей как родителя, так и наследника.

Interface segregation

Интерфейс не должен определять больше функциональности, чем используется за один раз. Это как Single Responsibility, только для интерфейсов. Если интерфейс описывает больше одной задачи, разбиваем его на несколько интерфейсов.

Dependency inversion

Класс должен объявлять зависимости (то, что ему нужно) через интерфейсы, но никогда не получать их самостоятельно.

Где-то это уже было...

Хорошие и плохие зависимости

  • Cohesion - связность.
  • Coupling - связанность.

Cohesion

Степень единства элементов модуля.

Coupling

Степень взаимной зависимости модулей / классов.

Cohesion - хорошо. Coupling - плохо.

  • Служащие одной цели классы собираются в модули (Не модули фреймворка! Не конкретные классы! Логические рамки).
  • Внутри модуля используем его классы напрямую. Нет смысла абстрагироваться.
  • Используемые модулем классы, но не связанные с ним, используются только через интерфейсы.
  • Модулю наплевать на то, как он получит зависимости.

Довольно много мест в Yii сделаны не по SOLID. На то были причины.

Чтобы нарушать правила необходимо их знать и уметь соблюдать.

Попробуйте делать в своих проектах правильно.

Про компромиссы...

Active Record

  • Делает одновременно слишком много.
  • Притягивает к себе и бизнес-логику и инфраструктурную логику.
  • Позволяет достичь цели очень быстро.
  • Удобен.

Переабстрагирование

Чрезмерное увлечение абстракцией может привести к тому, что каждый отдельный её уровень слишком прост, а взаимодействие уровней слишком сложно.

Недоабстрагирование

Лапша :(

Документирование архитектурных решений

Что описывать?

  • Модули: структура компонентов.
  • Компоненты и коннекторы: взаимодействие компонентов.
  • Размещение: физическое распределение компонентов по серверам.
  • Ответственность.

Чем описывать?

  • Текст. Как? Почему?
  • UML.
  • Простые диаграммы. Наример, в yEd.

Как сделать приложение надёжным?

Исправляю одно, отваливается другое...

Не увлекайтесь DRY.

Выделяйте границы.

Избегайте неявнях зависимостей.

Избегайте состояния.

TDD/BDD

Тестировать.

Хорошие и плохие тесты

Ключевые места хорошей архитектуры не должно быть сложно тестировать.

Засуньте тесты в CI!

Архитектура зависит в большей степени от предметной области и нужд компании, чем от применяемых фреймворков и библиотек.

Как отразить в архитектуре домен?

DDD

Domain Driven Design

Программист недостаточно знает автоматизируемый им процесс.

Есть человек, который знает его от и до. Это — доменный эксперт.

  • Большие богатые не логику проекты (> 6 месяцев).
  • Цель — получить архитектуру, близкую к реальному процессу.
  • Использовать термины реального процесса.

Структурные блоки

  • Value object — значение, которое может включать в себя другие значения.
  • Entity — объекты, которые можно идетифицировать. Взяв два объекта и сравнив их, можно определить, тот же это объект или нет.
  • Aggregate — группа объектов. Имеет главный Entity, без которого вся группа не имеет смысла (root). Транзакционно целостна.

Доменный слой не зависит ни от чего.

Главные паттерны

  • Repository — сохраняет чистые объекты и загружает их. Чаще всего речь идёт про базу данных. Не AR! Иногда его бьют на Command и Query.
  • Доменный сервис — использует entity для какой-то полезной работы. Всё ещё не зависит ни от чего вне домена.
  • Сервис приложения — предоставлятся фреймворком (components). Может использовать доменный сервис для работы с доменом.

DDD в чистом виде действительно нужен не часто

  • Сложно.
  • Оверхед.
  • Абстрактно.
  • Плохо натягивается на "бедный" домен.

* из слайдов Дмитрия Науменко

Изучать стоит!

Можно перенять частично

  • Понятные конкретные методы в AR, работающие с инстансом.
  • Отдельные модели для форм.
  • Сервисы.
  • ActiveQuery.

Главное

  • Абстракция — вынужденная мера.
  • Паттерны — не архитектура.
  • Фреймворк — не архитектура.
  • Практики и паттерны можно применять частично и по необходимости.

Думайте!

Почитать

Время вопросов!