Недавно я прочитал книгу «Архитектура сложных веб-приложений. С примерами на Laravel», автор Adel Faizrakhmanov.
Эта книга не пособие. Много шаблонов описаны поверхностно, с целью просто познакомить читателя с ними. Более подробное описание можно найти по ссылкам в конце.
Книга читается легко и с интересном, никакой воды, наоборот, иногда хочется продолжения и углубления в тему, больше примеров, но Адель оставляет нам это на самостоятельную проработку.
- https://github.com/adelf/acwa_book_ru — книга на русском
- https://leanpub.com/architecture-of-complex-web-applications — английская версия
Вот некоторые из интересных вопросов, рассмотренных в книге.
Какие ограничения несёт в себе мышление в стиле Create, Read, Update, Delete (CRUD)?
Забавно, что в одном из подкастов, который я когда-то слушал (уже не вспомню какой точно), создатель Laravel, Taylor Otwell подметил, что все его приложения отлично укладываются в модель CRUD, а если нужно что-то иное, какой-то отличное от этого действие, то скорее он что-то делает не так. Вот такая полярность мнений.
Опасность наследования и концепция: класс должен быть либо финальным, либо абстрактным. Сильно поддерживаю, не раз обжигался на наследовании конкретных реализаций и с опытом пришел к такому же выводу: если наследоваться, то только от абстрактного класса.
Польза от внедрения зависимостей и когда оно не нужно? А статические методы — есть ли для них место под солнцем?
Трейты. Адель рассмотрел трейты со всех сторон, когда они уместны и когда бесполезны.
Однако, приведу небольшую цитату, к которой у меня остались вопросы:
Я не люблю Трейты. Они могут с успехом использоваться в классах тестов, поскольку там нет хорошей причины организовывать полноценное DI, но лучше избегать их использования в главном коде приложения. Трейты — это не ООП, а чистые ООП решения всегда будут более естественными.
Тут хочется переспросить, что такое «чистые ООП решения»? Борцы за чистоту ООП скажут, что наше ООП — это вообще не ООП, которое имел в виду отец-основатель настоящего ООП Алан Кей! С другой стороны, напирают адепты настоящего ООП, прочитавшие книгу Elegant Objects Егора Бугаенко.
Поэтому я всегда насторожено отношусь к формулировкам, которые делают отсылки к определению ООП. Трейты — это не ООП? Зависит от вашей ООП конфессии!
Отлично описаны вопросы передачи данных запроса внутрь приложения с помощью DTO.
А также валидация. Валидация с помощью Form Requests, валидация непосредственно в сервисах, валидация с помощью аннотаций в Symfony, и валидация в конструкторах Value Objects.
Если мыслить объектами и грамотно применять Value Objects для представления данных — это всё усложнит или всё упростит, в чём профит?
Обработка ошибок, что выбрать — повсеместно использовать исключения или возвращать Optional значения? Вообще, возврат Optional имеет много плюсов и активно используется в функциональных языках программирования, но несколько чужд нам, в экосистеме PHP.
Про исключения Адель отлично разложил по полочкам чем отличаются проверяемые и не проверяемые исключения — это два типа исключений, которые явно реализованы в синтаксисе Java. В PHP явного деления нет, но PhpStorm даёт нам подсказки, опираясь на ту же модель проверяемых и не проверяемых исключений. Очень полезная глава для понимания, как обрабатывать различные типы ошибок.
События и чем неудобны события Eloquent. Лучше использовать свои собственные доменные события.
Тестирование. С Unti тестированием простых чистых функций всё просто, но что делать с тестирование сервисов, имеющих много зависимостей и как не утонуть в коде, конфигурирующем все эти зависимости в виде моков и стабов? Из-за желания написать Unit тесты на относительно сложные сервисы, иногда мы обрастаем излишними абстракциями, код самого приложения становится сложнее чем хотелось бы и чем он мог бы быть.
Что делать? Менять подход к написанию сервисов и отделить слой приложения от доменной логики. Читая книгу, мы от unti-тестирования сервисов плавно переходим к DDD и возвращаемся к unit-тестированию, но уже доменного слоя и тут всё встаёт на свои места. Только от этого не становится легче, т.к. это уже не похоже на типичное Laravel приложение. Кажется, что уже проще писать сразу на Symfony, чтобы все эти советы применить.
Вынесение доменной логики — это большой шаг в эволюции проекта. Намного более практично делать его в самом начале, но архитектор должен оценить сложность этой логики. Если приложение — это простой CRUD с очень небольшой дополнительной логикой, то от доменного слоя будет мало толку.
Вспоминая слова Тейлора, что он практически любую логику своих приложений успешно вписывает в CRUD, всё становится на свои места.
Последние две главы про CQRS и Event Sourcing с неплохими примерами.
Так что рекомендую к прочтению: «Архитектура сложных веб-приложений. С примерами на Laravel», автор Adel Faizrakhmanov. Книга доступна на русском и на английском.
В заключение, оставлю заключение к самой книге:
Эта книга просто некий обзор практик, которые мне показались полезными при разработке приложений. Возможно, кому-то она поможет выбрать нужную для своего проекта. Главное, понять, что книга не о том, что надо каждое приложение взять и переписать с использованием, например, Event Sourcing. К каждому приложению нужен свой подход и одни и те же практики отлично подходят к одним приложениям, но будут вредны для других.