Обложка: Стоит прочитать: обзор книги Роберта Мартина «Чистая Архитектура»

Стоит прочитать: обзор книги Роберта Мартина «Чистая Архитектура»

Антон Ярков

Антон Ярков

руководитель команды разработчиков в компании Access Softek Inc.

Не секрет, что одним из самых сложных в разработке программного обеспечения, является вопрос «как успешно вести разработку в условиях изменяющихся требований

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

Понятие Чистой архитектуры впервые Роберт Мартин (или, дядюшка Боб) опубликовал у себя в блоге ещё в 2012 году. В книге же, он подводит к этому понятию последовательно, в первую очередь развеивая миф о катастрофичности изменчивых требований. Чтобы сделать это, он вводит несколько важных понятий и определений.

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

Многие программисты полагают, что их задача заставлять компьютеры соответствовать требованиям и исправлять ошибки под отладчиком. Однако, на самом деле, программистов нанимают на работу, чтобы они заставили компьютеры экономить деньги или приносить прибыль. Это определяет поведение — одну из двух ценностей программы.

Но попробуйте ответить на вопрос: что лучше, программа, которая работает идеально, но её невозможно изменить при смене требований? Или же программа, которая не работает, но её легко поменять?

В книге даётся аргументированный ответ о том, что изменяемость программного обеспечения – его важнейшее свойство, которое исходит из самого определения английского слова Software: Soft — «гибкое / программное» + Ware — «обеспечение». Идея программного обеспечения состоит в том, чтобы дать простую возможность изменять поведение компьютеров. Это и делает ПО гибким. И это такое же требование, как и любые бизнес-требования, которые должны удовлетворить разработчики и архитекторы, создав соответствующую структуру (архитектуру) продукта.

Бизнес менеджеры не способны осознать важности архитектуры, потому что они «продают» поведение. По этой же причине они всегда ставят важность поведения на первое место и стараются повысить приоритет соответствующей работе. Автор же уверяет, что о важности качества архитектуры обязаны говорить разработчики, так, как только они наиболее полно представляют возможные негативные последствия того, что архитектуре не будет уделено достаточное внимание. Они должны бороться за неё. На самом деле им за это и платят.

Поэтому, эта вторая ценность (структура), сильнейшим образом влияет на дальнейшую судьбу проекта, хоть это сначала и трудно заметить.

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

Опять же, с точки зрения Бизнес Менеджера, он лишь создаёт постоянный поток новых требований одного и того же масштаба. Однако разработчикам кажется, что их вынуждают постоянно добавлять всё новые «костыли» в деградирующую систему. Но на самом деле, проблема в самой архитектуре, которая предусматривает предусматривает возможность внесения только одной формы изменений. Архитектуры должны быть максимально независимыми от формы. В дальнейших главах читатель узнает о том, как же этого достичь.

Мне понравилось, что автор последователен и начинает как бы издалека: он рассуждает про все известные нам парадигмы программирования и то, что важного они привнесли с точки зрения архитектуры систем. Он детально объясняет понятия зависимостей в программе, потока данных программы, а также касается возможностей инверсии зависимостей (Dependency Injection) в программе. В итоге он делает вывод о том, что самое важное что привнесла парадигма ООП (объектно-ориентированное программирование) – это возможность полного контроля над направлением зависимостей между частями и компонентами программы.

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

  • Принцип единственной ответственности (SRP).
  • Принцип открытости/закрытости (OCP).
  • Принцип Барбары Лисков (LSP).
  • Принцип разделения интерфейса (ISP).
  • Принцип инверсии зависимости (DIP).

Далее, автор подробно рассказывает про понятие компонент системы и рассказывает о трех принципах, на основе которых принимается решение к какому компоненту отнести тот или иной класс:

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

Эти три принципа определяют так называемую связность компонент (Component Cohesion). И следуя логике, дядюшка Боб описывает сочетаемость компонент (Component Coupling), рассказывая о трех принципах, определяющих правила взаимоотношений между компонентами:

  • Принцип ацикличности зависимостей (Acyclic Dependencies Principle).
  • Принцип устойчивых зависимостей (Stable Dependencies Principle).
  • Принцип устойчивости абстракций (Stable Abstractions Principle).

Знакомство с этими принципами подкрепляется практическими рекомендациями о метриках, которые можно высчитать и которые можно контролировать в системе.

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

По словам автора, для успешного существования и развития проекта, хорошая архитектура должна обеспечивать:

  • разнообразие вариантов использования (use cases);
  • эффективную работу системы;
  • простоту разработки системы;
  • простоту развертывания системы.

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

Когда архитектор стремится получить структуру системы, поддерживающую все необходимые варианты использования, он может не знать их все. В этом случае, опираясь на контекст назначения системы, он может использовать принципы единственной ответственности (SRP) и согласованного изменения (CCP) для отделения друг от друга всего, что изменяется по разным причинам, и объединения всего, что изменяется по одной причине.

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

По разным причинам обычно меняется пользовательский интерфейс, бизнес-правила программы, бизнес-правила самой предметной области, ну и конечно части, отвечающие за ввод-вывод, сетевой доступ или доступ к базам данных Автор называет это Уровнями. Причем, чем дальше от ввода-вывода, тем выше уровень.

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

Разделение на уровни и варианты использования даёт возможности как добавления новых вариантов использования системы, так и возможности независимой разработки и развёртывания. Иначе говоря – делает архитектуру гибкой.

Роберт Мартин называет это Режимом разделения. В пример таких режимов он приводит сервис-ориентированную архитектуру (SOA), основанную на службах, а также плагинную архитектуру.

Автор отмечает важность избегания преждевременных решений, которые не касаются бизнес-требований или вариантов использования, но являются чисто техническими, вроде выбора фреймворка, БД, веб сервера и т. д. Хорошая архитектура не зависит от таких решений. Чтобы добиться этого, например, в плагинной архитектуре границы между компонентами проведены так, что для одной и той же программы можно предоставить несколько разных пользовательских интерфейсов, настроить работу на разных БД, и т.д.

Что же происходит, когда границы компонент пересекаются? А ничего, это нормальный ход событий, когда, например клиент (высокоуровневый компонент) вызывает функцию сервера (низкоуровневого компонента). И совсем не спроста в начале книги подробнейше рассматриваются парадигмы программирования и каждый принцип проектирования: ведь вся хитрость создания правильных решений о пересечении границ между компонентами, заключается в управлении зависимостями.

Я заметил, что на протяжении всех глав, важности проведения границ в книге уделяется особое внимание. Приведены схемы и диаграммы того, как избегать прямой зависимости между классами или компонентами, например клиентом и серверов. Показано, как четкое отделение интерфейсов от реализации даёт возможность повернуть направление зависимости в обратную сторону потоку управления. Потоки управления и потоки данных, в хорошо спроектированной системе, всегда должны быть развязаны от зависимостей в коде, чтобы удовлетворить упомянутые выше требования: простоту разработки и развертывания системы.

Хочу отметить, что автор говорит о границах не только в коде и архитектуре, но и в бизнес-требованиях.  Он пишет, что бизнес-требования состоят из сущностей, часто описываемых на языке UML, а также вариантов использования, неформально описываемых в виде входных и выходных бизнес-данных. И те и другие не должны описывать низкоуровневые аспекты системы, вроде UI или фреймворка.

В итоге, сводя всё вышеизложенное воедино, Роберт приводит пример Чистой архитектуры, по его словам, объединяющей лучшие стороны большинства известных архитектур, таких как гексагональная архитектура, Data-Context-Interaction архитектура, и Boundary-Control-Entity архитектура.

В дальнейших главах автор разбирает чистую архитектуру в деталях, описывая способы пересечения границ, например используя DTO (Data-Transfer Objects). Он описывает шлюзы к слою доступа к базам данных и преобразователи реляционных данных (ORM). Я отдельно отмечу подробно разобранные паттерны проектирование, такие как:

  • View-Presenter;
  • Façade (Фасад);
  • Strategy (Стратегия);
  • Service (Служба);
  • CQRS;
  • Main component (анти-паттерн «Главный компонент»).

Наконец, автор приводит примеры использования принципов Чистой архитектуры в решении нескольких конкретных задач, таких как построение игрового движка (Game Engine), сервиса заказа такси (Taxi Aggregator) и сервиса продажи видео. Он также описывает возможности применения чистой архитектуры во встраиваемом ПО и операционных системах, а также проектирование интерфейсов и API для тестирования.

На мой взгляд, книга покрывает важнейшие аспекты дизайна и проектирования программ. В ней нет обзора конкретных фреймворков или языков программирования, но она показывает на что нужно обращать особое внимание, как закладывать гибкость, выявлять нарушения и контролировать структуру проекта. Так или иначе, любой разработчик столкнётся с необходимостью знания описываемых в книге принципов, но чем раньше он это сделает, тем меньше ошибок он совершит. Именно поэтому я рекомендую прочтение этой книги каждому разработчику, независимо от его уровня – от Junior до Senior и Architect.