Башня Байткода
69 subscribers
26 photos
2 files
27 links
Авторские заметки об архитектуре ПО на основе реальной практики.

github: https://github.com/PHPTCloud
Download Telegram
#arch #solid #note
Принцип подстановки Барбары Лисков — The Liskov Substitution Principle или LSP

Принцип в формулировке Роберта Мартина декларирует, что функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа не зная об этом. Оригинальное определение Барбары Лисков более формальное и заметно сложнее для восприятия: «В том случае, если q(x) — свойство, верное по отношению к объектам х некого типа T, то свойство q(y) тоже будет верным относительно ряда объектов y, которые относятся к типу S, при этом S — подтип некого типа T».

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

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

Наследник класса дополняет, но не заменяет поведение базового класса. То есть в любом месте программы замена базового класса на класс‑наследник не должна вызывать проблем. Если по каким‑то причинам так не получается, то вероятнее всего имеет место либо некорректная реализация, либо неверно выбранная абстракция для наследования.

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

Но LSP, как и любой другой принцип, не является догмой. И иногда следование этому принципу при построении архитектуры может приводить к более ресурсоёмкой реализации, нежели работа с нарушением этого принципа. Но как и с любыми другими правилами — надо осознавать возможные последствия нарушения.
👍4🔥3
#arch #solid #note
Принцип разделения интерфейса — The Interface Segregation Principle или ISP

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

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

В чём‑то принцип разделения интерфейса перекликается с принципом единственной ответственности — интерфейсы не должны быть избыточно «толстыми», если вдруг в приложении формируется слишком объёмный интерфейс, то есть высокая вероятность, что так происходит из‑за того, что в этом интерфейсе слишком много разных ответственностей, а значит логичнее всего провести декомпозицию сложного интерфейса на несколько простых.

Принцип разделения интерфейса снижает сложность поддержки и развития приложения. Чем проще и минималистичнее используемый интерфейс, тем менее ресурсоёмкой является его реализация в новых классах, тем меньше причин его модифицировать, но и в случае модификации она будет значительно проще.
👍4🔥3
#arch #note #solid
Принцип инверсии зависимостей — The Dependency Inversion Principle или DIP

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

Следование принципу инверсии зависимостей «заставляет» реализовывать высокоуровневые компоненты без встраивания зависимостей от конкретных низкоуровневых классов, что, например, сильно упрощает замену используемых зависимостей как по бизнес‑требованиям, так и для целей тестирования. При этом зависимость формируется не от конкретной реализации, а от абстракции — реализуемого зависимостью интерфейса.

Например, мы реализуем хранение документов в веб‑приложении. На первый взгляд, кажется логичным добавить зависимость от модулей работы с файловой системой непосредственно в класс, отвечающий за высокоуровневую работу с этими документами. Но в перспективе такая зависимость может создать проблемы — например, нам потребуется хранить данные не только на диске, но и в облаке. Если зависимость внедрена от реализации, то мы столкнёмся с необходимостью её переработки. Если же зависимость выведена на уровень абстракции (интерфейса), то нам будет достаточно реализовать функционал работы с облаком, соответствующий ранее созданному интерфейсу работы с файлами.

Принцип инверсии зависимостей часто упрощает следованию принципу подстановки Барбары Лисков. Выделение абстракций и встраивание зависимостей от них снижает вероятность того, что в новом классе мы не полностью реализуем контракт базового класса, который мы расширяем в рамках нового.
👍4🔥2
#arch #note #solid #solid4mens
Принцип Стабильных Зависимостей - The Stable Dependencies Principle или SDP

«Не получится строить стабильное на нестабильном»

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

Зацепление — мера того, насколько взаимозависимы разные подпрограммы или модули.

Сильное зацепление рассматривается как серьёзный недостаток, поскольку затрудняет понимание логики модулей, их модификацию, автономное тестирование, а также переиспользование по отдельности. Слабое зацепление, напротив, является признаком хорошо структурированной и хорошо спроектированной системы.
👍4🔥3🤯3
#arch #solid #sold4mens #note
Принцип стабильных абстракций - The Stable Abstraction Principle или SAP

Стабильные пакеты абстрактны. То есть абстрактность должна возрастать со стабильностью. Стабильность здесь — то, как часто нам приходится менять части пакета: классы, интерфейсы, или что-либо ещё. Абстрактные пакеты должны быть стабильны, чтобы безболезненно на них завязываться.

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

❓ Можно ли измерить абстрактность?

Да. Абстрактность — это число абстрактных классов и интерфейсов в пакете, деленное на общее число классов и интерфейсов в этом самом пакете.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥3🤯3
#arch #solid #solid4mens #note
Принцип ациклических зависимостей - The Acyclic Dependencies Principle или ADP

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

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

Другой пример: вы написали "Модуль Б", который зависит от "Модуля А" и использует функции "Модуля В". "Модуль В" в свою очередь зависит от "Модуля А". Если вы или ваши коллеги внесут изменения в "Модуль Б", то эти изменения затронут "Модуль В", а он затронет код "Модуля А", который затронет код "Модуля Б"... и начнется типичная дилемма курицы и яйца. 🤯

Следование принципу ADP решает эту дилемму и другие проблемы:

1️⃣ Сложность тестирования и обслуживания компонентов системы;

2️⃣ Циклические ссылки на классы создают высокую степень связи; оба класса должны перекомпилироваться каждый раз, когда любой из них изменяется;

3️⃣ Объекты с очень большим количеством циклических ссылок часто являются объектами-богами. Даже если это не так, они имеют тенденцию приводить к спагетти-коду.

* на постере пример системы с циклическими зависимостями.
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯5👍2
#exp #arch #grasp
Проектирование пакетов/компонентов системы

На ряду с принципами SOLID, Роберт Мартин описал также 6 других:

1️⃣ Принцип эквивалентности повторного использования и выпуска (Reuse-Release Equivalency Principle или REP);

2️⃣ Общий принцип закрытия (Common Closure Principle или CCP);

3️⃣ Общий принцип повторного использования (Common Reuse Principle или CRP);

4️⃣ Принцип ациклических зависимостей (Acyclic Dependencies Principle или ADP);

5️⃣ Принцип стабильных зависимостей (Stable Dependencies Principle или SDP);

6️⃣ Принцип стабильной абстракции (Stable Abstraction Principle или SAP).

Возможно, вы уже слышали рекомендацию, в которой говорится, что мы должны стремиться к достижению low coupling (низкой связанности) и high cohesion (высокого сцепления) при работе над кодовой базой.
Мне не нравится термин "сцепление", поэтому я буду использовать слово "сплоченность".

❗️ Coupling (связанность) представляет собой степень взаимосвязи между блоками. Другими словами, это количество соединений между двумя или более блоками. Чем меньше число, тем ниже coupling. Блок (юнит) здесь необязательно является классом. Это может быть метод, класс, группа классов или даже модуль.

Понятие Cohesion (сплоченность) мы разберем чуть позже, когда узнаем о принципах REP, CCP, CRP. Для удобства эти принципы разделяют на следующие группы:

1️⃣ Принципы связности пакетов/компонентов - REP, CCP, CRP - снижают степень связности (coupling) пакетов и компонентов;

2️⃣ Принципы сплоченности пакетов/компонентов - SAP, SDP, ADP - увеличивают степень сплоченности (cohesion).

* На постере идеальный пример сплоченности модулей и сцепления компонентов.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍3
#arch #note #solid4mens #solid
Принцип эквивалентности повторного использования и выпуска - The Reuse-Release Equivalency Principle или REP


❗️ Принцип гласит, что единица повторного использования не может быть меньше единицы выпуска. Все, что предназначено для повторного использования, должно управляться какой-то системой учета выпусков. Не может быть так, чтобы разработчик просто написал какой-то класс и объявил его повторно используемым. О возможности повторного использования можно говорить только тогда, когда внедрена некая система учета выпусков и обеспечены гарантии извещения, надежности и поддержки, в которых так нуждаются потенциальные пользователи.

❗️ Эффективное повторное использование требует отслеживания релизов в системе контроля изменений

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

❗️ Единица повторного использования равна единице выпуска.

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

Пакет — это эффективная единица повторного использования и выпуска.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍3
#arch #note #solid4mens #solid
Общий принцип закрытия - The Common Closure Principle или CCP

❗️ Общий принцип закрытия гласит, что классы, которые изменяются по одним и тем же причинам и в одно и то же время, должны быть собраны в компоненты, а классы, которые изменяются в разное время и по разным причинам, должны быть разделены на разные компоненты.

Этот принцип представляет собой переформулировку принципа единой ответственности (SRP), рассмотренного в SOLID , путем применения его к компонентам. Точно так же, как SRP утверждает, что у класса не должно быть более одной причины для изменения, CCP утверждает, что у компонента не должно быть более одной причины причины для изменения.

❗️ Когда необходимо внести изменения в код, идеально, чтобы эти изменения происходили только в одном компоненте, а не в нескольких.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍3
#arch #note #solid4mens #solid
Общий принцип повторного использования - The Common Reuse Principle или CRP

❗️ Принцип совместного повторного использования (CRP) помогает определить, какие классы и модули должны быть включены в компонент. Он говорит, что классы и модули, которые используются вместе, должны быть включены в один компонент.

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

Иногда, даже если в использующем компоненте ничего не меняется, его все равно нужно перекомпилировать, тестировать и развертывать заново.

❗️ Компонент не должен зависеть от того, что ему не требуется.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍3
#arch #solid #note #solid4mens
Диаграмма противоречий для определения связности компонентов

Три принципа связности компонентов вступают в противоречие друг с другом:

1️⃣ Принципы эквивалентности повторного использования (REP) и согласованного изменения (CCP) являются включительными: оба стремятся сделать компоненты как можно крупнее.

2️⃣ Принцип повторного использования (CRP) — исключительный, стремящийся сделать компоненты как можно мельче. Задача хорошего архитектора — разрешить это противоречие.

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

❗️ Архитектор, уделяющий внимание только принципам REP и CRP, обнаружит, что простое изменение вовлекает слишком большое количество компонентов. С другой стороны, архитектор, уделяющий особое внимание принципам CCP и REP, вынужден будет выпускать слишком много ненужных версий.

❗️ Хороший архитектор найдет в этом треугольнике противоречий золотую середину, отвечающую текущим нуждам разработчиков, а также подумает об изменениях, которые могут произойти в будущем. Например, на ранних этапах разработки проекта принцип CCP намного важнее, чем REP, удобство разработки важнее удобства повторного использования.

Вообще говоря, в начале разработки наибольшую важность имеет правая сторона треугольника, когда единственной жертвой является повторное использование. Но по мере развития и интеграции в другие проекты фокус начинает смещаться влево. То есть организация компонентов в проекте может изменяться с течением времени. Это больше связано с тем, как проект разрабатывается и используется, нежели с тем, что фактически этот проект делает.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2🤯2
#arch #book
Чистая архитектура. Искусство разработки программного обеспечения

Автор: Роберт Мартин
Год выпуска: 2017-2018
Страниц: 352
👍4🔥3
Башня Байткода pinned «#gex #navi Первая цепочка постов о Гексагональной архитектуре подошла к концу. Надеюсь, вы получили много полезой информации о том, как работать с рассмотренным паттерном. Чтобы глубже понять Гексагоналку, нам потребуется больше времени, а пока продолжим погружаться…»
#exp #arch #case
Хочу поделиться кейсом задачи, которую недавно решил в Github Booster. Цель - продемонстрировать, как можно развивать отдельные модули, как можно мыслить, чтобы дать импульс такому развитию.

📌 Задача

1️⃣ Использовать два кейса - syncSubscribers и syncSubscriptions для подписки на пользователей, которые подписались на целевого пользователя в Github, но на которых я не подписался в ответ.

2️⃣ Использовать два кейса - syncSubscribers и syncSubscriptions для отписки от пользователей, на которых подписался целевой пользователь, но которые не подписались на меня в ответ.

Процесс развития модулей

▶️ Уровень 1

Нарушен принцип SRP - класс SyncManager может изменяться более чем по одной причине. Декомпозируем.

▶️Уровень 2

Можно так и оставить, но мне бы хотелось абстрагироваться от каждого кейса, поэтому через Manager классы я буду вызывать Handler классы. Это поможет мне в будущем заменить один Handler на другой так, чтобы те, кто использует Manager класс, не знали об этом измении и продолжали свою работу.

▶️ Уровень 3

Все, что хотели, разделили и абстрагировали. И тут возникла циклическая зависимость, которая порождается методами subscribersBalancing и subscriptionsBalancing. SubscribersManager класс зависит от SubscriptionsManager и наоборот.

▶️ Уровень 4

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

Cоздаю интерфейсы SubscribersSyncManagerInterface и SubscriptionsSyncManagerInterface, чтобы Manager классы завязывались на них (выделены зеленым).

Эти интерфейсы в моем случае ведут к еще одним Manager классам, которые нужны для отдельной логики.

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

Плюсы и минусы

➕ Соблюдается принцп SRP;
➕ Есть Handler классы, которые удобно покрывать тестами;
➕ Через Dependency Injection можно переопределять детали реализации интерфейсов.

❌ Много классов;
❌ Структура проекта без конкретных правил может стать чрезмерно запутанной.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥4🤔1
#arch #fsd #note
Дизайн с нарезкой деталей - Feature-Sliced Design или FSD

❗️ Feature-Sliced Design (FSD) — это архитектурная методология для проектирования frontend-приложений. Проще говоря, это свод правил и соглашений по организации кода. Главная цель методологии — сделать проект понятным и структурированным, особенно в условиях регулярного изменения требований бизнеса.

Проект на FSD состоит из слоев (layers), каждый слой состоит из слайсов (slices) и каждый слайс состоит из сегментов (segments).

❗️ Слои

Слои стандартизированы во всех проектах и расположены вертикально. Модули на одном слое могут взаимодействовать лишь с модулями, находящимися на слоях строго ниже. На данный момент слоев семь (снизу вверх):

1️⃣ shared - переиспользуемый код, не имеющий отношения к специфике приложения/бизнеса.

2️⃣ entities - бизнес-сущности.

3️⃣ features - взаимодействия с пользователем, действия, которые несут бизнес-ценность для пользователя.

4️⃣ widgets - композиционный слой для соединения сущностей и фич в самостоятельные блоки.

5️⃣ pages - композиционный слой для сборки полноценных страниц из сущностей, фич и виджетов.

6️⃣ processes (процессы, устаревший слой) - сложные сценарии, покрывающие несколько страниц.

7️⃣ app - настройки, стили и провайдеры для всего приложения.

❗️ Слайсы

Затем есть слайсы, разделяющие код по предметной области. Они группируют логически связанные модули, что облегчает навигацию по кодовой базе. Слайсы не могут использовать другие слайсы на том же слое, что обеспечивает высокий уровень связности (cohesion) при низком уровне зацепления (coupling).

❗️ Сегменты

В свою очередь, каждый слайс состоит из сегментов. Это маленькие модули, главная задача которых — разделить код внутри слайса по техническому назначению.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2🤔2
#highloadpp #video
Всем привет 👋

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

https://youtu.be/WWTq-tbZwUE?si=52nSg02EIUDFlGCJ
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍3
#arch #ddd
Предметно-ориентированное проектирование - (The Domain-Driven Design или DDD)

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

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

1️⃣ чаще возникает путаница в требованиях;

2️⃣ сложно поддерживать аналитические артефакты, которыми пользуется заказчик и разработчики;

3️⃣ появляется устаревшая документация, которая может вводить в заблуждение новых разработчиков;

4️⃣ появляется много лишнего кода.

В следующей цепочке постов я расскажу, как можно решать такие проблемы с использованием Предметно-ориентированного проектирования (DDD).

❗️ Предметно-ориентированное проектирование - это методология, которая предлагает последовательный алгоритм проектирования бизнес моделей системы и её бизнес правил.

В рамках этой методологии мы рассмотрим:

1️⃣ Как бизнес модель и её правила могут влиять на архитектуру приложения;

2️⃣ Принципы, формирования единого языка, на котором говорят все участники проекта;

3️⃣ Процессы эффективной дистилляции знаний о модели и её правилах.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍3🤯1
#arch #ddd
Модель предметной области

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

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

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

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

Основные принципы использования модели при разработке:

1️⃣ Модель и архитектура программы должны взаимно определять друг друга. Именно тесная связь модели и её реализации определяют важность и нужность самой модели, а также гарантирует, что проделанный анализ модели отражен в её реализации - в программе;

2️⃣ Модель лежит в основе языка, на котором говорят все участники проекта. Благодаря связи модели с её реализацией разработчики могут обсуждать все связанные с программой вопросы на этом языке и общаться со специалистами предметной области без переводчика;

3️⃣ Модель - это дистиллированое знание. Она отображает только аспекты и идеи, представляющие наибольший интерес. Модель не должна содержать бесполезные или вторичные детали, знание которых не приносит существенной пользы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3🤯1