На главную

Книги по проектированию и архитектуре приложений

23-10-2019

Недавно я ответил на вопрос про Хранилище (Repository) на русском Stack Overflow. Автор вопроса попросил порекомендовать литературу по дизайну и архитектуре приложений. В рамках комментария отвечать оказалось неудобно, и я написал эссе о полезных книгах.

Приёмы объекто-ориентированного проектирования

Приёмы объекто-ориентированного проектирования

Книга, известная в народе, как GoF или главная книга о паттернах. Я не нашёл в интернете информации, как познакомились авторы и почему они назвали себя бандой четёрёх. Знаю, что все они в то или иное время работали в IBM. Эрих Гамма вместе с Кентом Беком разработал JUnit — предтечу всех современных фреймворков модульного тестирования. Если вы пишите модульные тесты, вы знаете, кого за это благодарить. Джон Влисидис умер в 2005-м в возрасте сорока четырёх лет.

Хелм и Джонсон не участвовали в известных мне проектах.

С момента выхода первого издания прошло 25 лет. Что мы узнали за это время?

  1. Термин паттерн трактуется слишком широко. Я слышал про паттерны многозвенное приложение, инверсия зависимостей и микросервис-заместитель (proxy microservice). Ничто из этого я бы к паттернам не отнёс. Автор оригинальной концепции Кристофер Александр говорит не просто о паттернах, а об языке паттернов. Язык предполагает общение и понимание, поэтому я против того, чтобы паттернами называли что угодно. Многозвенная архитектура — это модель, а инверсия зависимостей — принцип. Авторы книги дали паттернам узкое определение, ограничившись объектно-ориентированными языками. Они сформулировали ясную нижнюю границу — паттерном не может быть то, что входит в стандартную библиотеку. Нет паттернов массив и поток.

  2. Шестнадцать из двадцати трёх оригинальных паттернов могут быть выражены простыми языковыми средствами, что и продемонстрировал Петер Норвиг в своей статье. Языком программирования был не C++, а LISP. Получается, что некоторые паттерны — это просто досадная необходимость, вызванная слишком низким уровнем языка. Впрочем, в функциональных языках могут быть свои паттерны. Кроме того, паттерн может быть всё-таки языковым средством, который говорит о назначении объекта. Видя в коде Хранилище пользователей (User Repository) мы понимаем, что пользователи хранятся в базе, но нам, слава богу, не надо ничего знать об этой базе. Мы концентрируемся на бизнес-логике.

  3. Паттерн Одиночка (Singleton) на поверку оказался антипаттерном. Он нарушает принцип единственной ответственности, потому что экземпляр одиночки выполняет свою основую функцию и одновременно управляет временем своей жизни. Избавиться от одиночки можно, передав управление временем жизни в IoC-контейнер. Термин антипаттерн прочно вошёл в наш лексикон вместе с паттерном.

  4. Язык паттернов оказался полезным. Я ни разу в жизни не использовал Интерпретатор (Interpreter) и трепещу перед Посетителем (Visitor), считая его сложным. Я с удовольствием использую Единицу работы (Unit of Work), Хранилище (Repository), Корень композиции (Composite Root), Внедрение через свойство (Property Injection) и десятки других паттернов.

Резюме

На определённом этапе профессионального становления книгу надо обязательно прочитать. Названия паттернов надо использовать в своей речи и в своём коде. Важно не путать паттерны друг с другом, потому что это и правда язык. Неправильный язык ведёт к заблуждениям.

Обращайте внимание на источники. Если ваш друг Жора придумал паттерн, это не паттерн. Настоящий паттерн, как элемент общего языка, должен быть описан в популярной книге или в статье на посещаемом ресурсе. Паттерн должен быть описан в классической форме, то есть как в GoF, с разделами Название, Назначение, Реализация, Плюсы и минусы.

Словосочетание паттерн микросервис в какой-то статье из интернета не делает микросервис паттерном.

Шаблоны корпоративных приложений

Шаблоны корпоративных приложений

Основной автор книги Мартин Фаулер. У него пять соавторов, про которых я ничего не слышал. Google подксказывает, что у одного из них есть забытий профиль на GitHub, другой последние десять лет работает управленцем, а третий что-то там из Oracle.

Не смотря на таинственность авторов, книгу следует признать самой полезной книгой о паттернах. У неё есть сокращённое название P of EAA, то есть Patterns of Enterprise Application Architecture. Если GoF вводит нас в тему, то P of EAA погружает нас в самую пучину.

Рассмотрим на примере. В Entity Framework доступ к данным осуществляется через класс DbContext.

В DbContext изменения в базу вносятся не тогда, когда вы обновляете поля сущностей или вызываете методы удаления, а когда вызываете SaveChanges. Объект накапливает сделанные вами изменения и затем разом их вносит. Вам не приходится следить за тем, чтобы записи добавлялись или удалялись в правильном порядке — DbContext делает это за вас. Все ваши составные операции атомарны — если одна из них завершится ошибкой, отвергнуты будут все накопленные изменения.

На уровне базы данных мы называем такие операции транзакциями, но транзакции слишком детальны для слоя бизнес-логики. Мы можем игнорировать всё, что относиться к уровням изоляции или к вложенности. Паттерн Единица работы (Unit of Work) нужен как раз для этого и DbContext его реализует.

Почему в DbContext нет групповой операции удаления? Потому что в Unit of Work нет такой операции. Как обычно, паттерн хорош в одних сценариях, и не очень хорош в других. Полезно знать его ограничения, чтобы делать свою программу эффективной.

Entity Framework умеет преобразовывать деревья выражений в SQL-запросы. Дерево выражений в данном случае — это Объект-запрос (Query Object), паттерн, описанный Фаулером.

После загрузки данных, Entity Framework заполняет поля объектов из базы. Задача эта нетривиальная. Фаулер описыват набор классов, необходимых для отображения как паттерн Преобразователь данных (Data Mapper). Наряду с преобразователям, в программах регулярно встречаются Активная запись (Active Record), Шлюз к данным записи (Row Data Gateway), Шлюз к данным таблицы (Table Data Gateway) и другие. Их нет в Entity Framework, но вы найдёте их в ADO.NET или zend-db.

Резюме

Эту книгу надо прочитать, чтобы понимать, что встречается в современной бизнес-разработке. Не обязаетльно уметь готовить все эти паттерны, достаточно иметь представление о разнообразии решений.

Внедрение зависимостей в .NET

Внедрение зависимостей в .NET

Инверсия зависимостей (Dependency Inversion) — не самая простая концепция. Её трудно объяснить на пальцах, и если вы поняли её на пальцах, возможно, вы поняли её неправильно.

Инверсия зависимостей — старая идея, которую можно встретить даже в стандартной библиотеке C, а именно в функциях bsearch и qsort. Наверняка, это не самый ранний пример, но языку C в 2019-м году исполняется 50 лет, и это значит, что DI, как принцип, известен в мире программирования уже очень давно.

Чтобы принцип работал, нужно придумать, как внедрять зависимости. В C это делается через указатель на функцию с известной сигнатурой, в объектно-ориентированных языках — через параметры конструктора, свойства класса или параметры методов. Паттерны Constructor Injection, Property Injection и Method Injection как раз об этом.

Помимо общих принципов важны, конечно, и детали. Симан показывает, как внедрять зависимости вручную, как внедрять их через Castle, Autofac, Unity. Этого достаточно, чтобы перестать беспокоиться о конкретных библиотеках. Они похожи.

Резюме

Локатор служб (Service Locator) — это антипаттерн, а не паттерн. Книгу надо прочитать, если вы пишете на C#. Если вы пишете на другом объектно-ориентированном языке, она тоже может быть полезна.

Искусство автономного тестирования с примерами на С#

Искусство автономного тестирования с примерами на С#

Как писать модульные тесты (unit tests)? Есть несколько рекомендаций, которые, к сожалению, трудно применять на практике. Когда я начинал осваивать автоматическое тестирование, я по два часа зависал на тестах. Мне казалось, что для тестирования вот этой вот штуки мне потребуется другая штука, которую тоже нужно написать.

С тестами не задавалось. После того, как они были написаны, их приходилось постоянно переписывать. Да, я знаю, что тесты должны быть устойчивы. Спасибо.

Оказалось, что модульное тестирование, если его готовить правильно, требует серьёзного изучения. На помощь, конечно, приходит книга, и в случае юнит-тестов это книга Роя Ошероува. Причём здесь архитектура, спросите вы? Архитектура притом, что при написании тестов вам придётся делать классы маленькими и слабо сцепленными.

Резюме

Книга полезна и для того, чтобы практически освоить искусство написания модульных тестов, и для того, чтобы практически освоить принципы SOLID.

Чистая архитектура

Чистая архитектура

Последняя в списке, но не по значению.

Роберт Мартин, он же дядюшка Боб, известен нам с давних пор. Был такой журнал — C++ Report, который выходил c 1989 по 2002 год. Время рассвета C++. Именно там появились первые современные практики проектирования, до того, как придумали Java.

Дядюшка Боб был редактором этого журнала. Он сформулировал принципы SOLID. Он участвовал в обсуждении Манифеста гибкой разработки.

Он написал книгу о чистой архитектуре. Главная идея книги — кто управляет зависимостями, тот управляет миром. Чтобы проект оставался подконтрольным, снижайте количество зависимостей. Организуейте зависимости, без которых нельзя.

Важное знание, которое я получил из книги — никто не знает будущего. Никто не знает, что потребуется от программы через два года. И никакие ваши усилия не позволят вам придумать архитектуру, способную поддержать изменения, которые потребуются через два года. Каким бы умным вы ни были, вы ошибётесь.

Резюме

Книгу стоит перечитывать время от времени. С каждым новым проектом ваш опыт растёт, и этот опыт надо реструктурировать с помощью специальной литературы. Дело не в том, чтобы узнать новое. Дело в том, чтобы устаканить то, что уже есть в голове.