Текстовый вариант видеоурока из предыдущего шага План: Из истории создания подходов; Виды подходов. Из истории создания подходов Первые разработчики и новички в индустрии IT начинают разрабатывать код без проектирования и выстраивания архитектуры. Это достаточно удобный подход, так как не несет никаких издержек, связанных с построением архитектуры, проведением границ, с ранним проектированием. Однако со временем такой подход усложняет разработку, теряет гибкость. В целом будет сложно развивать систему за рамками тех границ, которые были наложены в начале написания кода. Поэтому за год до развития программного обеспечения, разработчики, компания и сама индустрия, выработали надежные проверенные подходы. Мы знаем о том, когда их применять, знаем об их плюсах и минусах. Так их начали называть «Подходы к разработке архитектур и программных обеспечений». Виды подходов Многослойная архитектура; Многоуровневая архитектура; Гексагональная архитектура; Монолит; SOA (Service Oriented Architecture); Микросервисы; СQRS, Event Sourcing. Не стоит рассматривать данные подходы как конкурирующие, зачастую в больших системах можно заметить в разных частях или даже в одном компоненте различные подходы. В определенных случаях можно увидеть наличие нескольких архитектурных подходов, также одну и ту же задачу можно решить несколькими способами, каждый из которых может приносить свои плюсы. Смотря на какую-то систему, старайтесь не вычленять только определенный подход и видеть только его. Конечно, какой-то из этих подходов будет ведущим, но это не противоречит тому, что в конкретной системе можно увидеть отпечатки или шаблоны из других подходов. Многослойная архитектура (Layered) Смысл данной архитектуры заключается в том, что мы всю функциональность нашей системы делим на слои по принципу разделения обязанностей. Основная особенность архитектуры в том, что каждая функциональность по своей ответственности должна относиться к какому-то слою, и каждый слой может взаимодействовать с предыдущим слоем, если он там есть. Рассмотрим слои на слайде. Это часто встречающиеся слои в многослойной архитектуре. Все начинается со слоя Presentation, на нем же будет находиться UI и пользовательский интерфейс. Далее идет слой Software service layer (сервисный слой). Здесь будет вся бизнес-функциональность, бизнес-логика и прочая функциональность, отвечающая за работу, цели и задачи нашей системы. У этого слоя может быть свой программный интерфейс для того, чтобы с системой взаимодействовали другие компоненты. Далее идут слои Data access layer и Integration layer. Первый из них отвечает за логику доступа к хранилищам, предоставляющим данные (резистентные хранилища — базы данных, файлы), второй из них отвечает за логику интеграции с другими системами. Следом идет Data layer. Это уже слой хранения данных. Здесь могут быть базы данных, файловые хранилища или просто файлы/блобы (blob). Далее идет Operation layer — самый низкоуровневый слой. Это слой доступа персон-системы, системных вызовов, драйверов, доступ к железу и т.д. Плюсы и минусы Плюсы: Простота; Декомпозиция ответственности; Тестируемость. Минусы: Проблемы масштабирования; Транзитивность данных; Усложнение внесения модификаций. Данная архитектура подходит для небольших компонентов и систем, где не так много логики. Она хорошо декомпозируется по ответственности. Многослойная архитектура хорошо подходит для подобных систем. Многоуровневая архитектура (n-tier) Рассмотрим слайд и то, из чего он состоит. Если у нас будет приложение, которое полностью работает на одной физической машине, то есть там будет UI, бизнес-логика, база данных. Если она будет работать на стороне клиента, то она может называться одноуровневой архитектурой, приложением с одним уровнем — Tier 1, который полностью работает на клиентской стороне. Если мы добавим к приложению какой-то сервер приложений, который будет обеспечивать коллаборативную работу нескольких клиентов или будет хранить данные/бизнес-логику, то это уже будет двухуровневое приложение, потому что у нас будет два уровня: часть приложения будет работать на стороне клиента и серверное приложение, с которым клиентское приложение обязано работать для своей функциональности. Если мы еще вынесем отдельные хранилища на базы данных, то у нас получится классическая трехзвенка — трехуровневая архитектура, которая будет состоять из клиентской части, сервера приложений и базы данных. Плюсы и минусы Плюсы: Простота; Понятное масштабирование; Целостность данных. Минусы: Проблемы с безопасностью; Проблемы с доступностью. Если у нас будет много бизнес-логики на стороне клиента, а сервер будет отвечать только для работы с базой данных или взаимодействовать лишь с небольшой логикой, то такие приложения называют «Толстый клиент». Если у нас основной сервер будет содержать основную бизнес-логику, а клиентское приложение будет отвечать за UI, за общение с сервером, то такие приложения называют «Тонкий клиент». Монолит Это традиционная модель программного обеспечения, где вся система представляет единый и неделимый модуль. Говоря о монолите, многие воспринимают его в отрицательной коннотации. Монолит — это не целевая архитектура, а просто способ декомпозиции/развертывания. Потому что в монолите можно успешно реализовать чистую архитектуру, таким образом, монолит и код в нем будут обладать всеми достоинствами чистой архитектуры. Конечно, можно сделать сильно связанный код, без тестов, но тогда монолит действительно превратится в «ком грязи». К монолиту нужно относиться как к способу разворачивания и способу декомпозиции развертывания. Минимальная единица развертывания — весь монолит, а не нечто плохое, чего нужно избегать. У монолита есть множество преимуществ: единая кодовая база, то есть один репозиторий, его легко разрабатывать, проект запускается, скорее всего, в одной IDE. Там работают все встроенные рефакторинги из IDE, вы можете видеть все тесты, можно заменить и найти по проекту все зависимости, необходимые вам, к какой-то функции. Если вы меняете функцию, вы видите все зависящие от нее. Отпадает необходимость поиска чего-либо по строкам. Помимо этого, у монолита простое развертывание, например, если мы хотим горизонтально отмасштабировать монолит, то мы просто будем разворачивать дополнительные инстансы одного и того же единого блока (например, Docker контейнера). Однако нам нужно будет думать, как масштабировать доступ к базе данных, масштабировать доступ к монолиту через балансировщики, но здесь проблемы такие же, как во всех остальных архитектурах. У монолита высокая производительность, потому что код компилируется в рамках одного процесса компиляции, размер итогового файла может получиться меньше, чем если мы будем компилировать различные микросервисы, где каждый микросервис будет иметь одну и ту же библиотеку, и, тем самым, размер файла будет расти. Однако на сегодняшний день размер файла не является проблемой, так как все хранится в «облаках». Производительность здесь будет намного выше, чем у других распределенных архитектур. Это из-за того, что все будет работать в одном процессе, обмен данными будет происходить между функциями через память. Стоит выбирать монолит, когда вы знаете, до какого размера будет расти кодовая база. То есть, здесь будет ограниченный размер кодовой базы или ограниченный жизненный цикл. Вам нужно будет быстро что-то разработать, разработать и зарефакторить, и задеплоить, монолит вам подойдет для прототипов, стартапов, чтобы проверить гипотезы. Если вы разрабатываете проект на долгие годы, которые будут дорабатываться командами разработки, рефакториться, кодовая база будет пополняться сотнями тысяч кодовых строк ежегодно, то монолит скорее всего сделает вам «больно», потому что текучка разработчиков, недостаток компетенций в проектировании, дадут о себе знать, когда у вас появится большой «комок грязи». Монолит, в отличие от других распределенных архитектур не дает возможности технически выстроить границы, которые разработчику будет сложно разрушить. В монолите просто «наломать дров» из-за того, что никто нас не ограничивает. Если же проект небольшой и с ограниченной функциональностью, с размером кода, который вы можете спрогнозировать, а также с компетентными разработчиками, то его можно использовать. Плюсы и минусы Плюсы: Простая разработка; Простое развертывание; Производительность. Минусы: Сложно сопровождать при разрастании кодовой базы; Ограниченная гибкость. CQRS, Event Sourcing CQRS — это подход, предлагающий нам разделять команды, которые изменяют данные, и запросы, которые данные читают. CQRS ( Command and Query Responsibility Segregation) — разделение системы на части, которые отвечают за команды и за запросы. На схеме мы можем увидеть, что система архитектуры будет делиться на две части: Одна будет хранить команды или записи в удобной для записи модели. Эти изменения могут синхронизироваться в базу данных для чтения и уже представляться клиенту в модели удобной для чтения. Здесь есть The Command Responsibility — верхняя часть, которая в удобной для записи/изменения модели хранит наши изменения. Вторая — Query Responsibility, когда в удобной для чтения модели будут храниться и читаться данные. Выводы: Архитектурных подходов много (на данном уроке мы рассмотрели лишь основные). Помимо этого, цель данного урока была не в том, чтобы научить вас проектировать с помощью пяти подходов, а в том, чтобы эффективно решать задачи, которые будет ставить индустрия/работодатель/заказчики. Необходимо будет выбирать наиболее уместный подход, и с помощью него решать проблему. Эффективнее — не значит быстрее и дешевле, это значит — компромисс. Нет «серебряной пули». Нет ни одной архитектуры, где были бы только плюсы. Архитектурный вопрос — это компромисс. Гексагональная архитектура Это изображение похоже на модель чистой архитектуры. Здесь есть такие же концентрические области, и все зависимости в этой архитектуре тоже направляются снаружи внутрь. Внутри находятся доменные модели, предметная область, бизнес-логика. Снаружи — базы данных, UI и т.д. Почему данная архитектура называется «Гексагональной»? Изначально автор в своей архитектуре выявил некоторые грани, например, грань, отвечающую за Persistence (базы данных), уведомления, административный UI, Business events. Так получилось, что каждая грань отвечает только за свою функциональность. Это стало похоже на шестиугольник, отчего и появилось название «Гексагональная архитектура». Основная идея данной архитектуры Данная архитектура неспроста еще называется «Архитектурой портов и адаптеров». Каждая грань нашего слоя должна представляться во внешнем мире для внешних слоев с помощью портов (о таком паттерне мы говорили в предыдущем модуле, в чистой архитектуре используются такие же понятия), а уже к порту внешние слои должны подключаться при помощи адаптеров. Помимо этого, в данной архитектуре автор предлагает разделить ее на две части: в левой части у нас будут UI компоненты на внешнем слое, в правой части — системные компоненты (базы данных и пр.). Смысл этой архитектуры, как и смысл чистой архитектуры в том, чтобы мы могли сделать слабосвязанную систему, чтобы архитектуру можно было легко расширять и модифицировать. Причина в том, что каждый компонент не так сильно зависим от других, как, например, в монолите или там, где в архитектуру не заложена явная возможность разделения. Помимо этого, здесь нужно обращать внимание на следование всем принципам SOLID. Плюсы и минусы Плюсы: Простота сопровождения; Легко параллельно разрабатывать. Минусы: Много дополнительных сущностей (код портов/адаптеров); Требуются прокаченные навыки проектирования. SOA (Сервис-ориентированная архитектура) Данная архитектура была придумана и описана в конце 1980 г. как решение возникающих проблем. В то время были предприятия, внутри которых было разработано много систем или программ, делающих полезные бизнес-функции, но их необходимо было объединять. Для того чтобы их объединить и решить проблему интеграции, была описана и придумана Сервис-ориентированная архитектура в ответ на возникающий вызов интеграции множества функций. Если посмотреть на ее структуру, то сразу можно начать с бизнес-функции, которая называется «Сервис». Это не просто микросервис, здесь имеется в виду определенная часть работы в рамках общего бизнес-процесса или сама бизнес-функция, которая состоит из контракта, реализации и публичного интерфейса. Реализация же состоит из бизнес-логики и данных. Это классическое понимание услуги. Помимо этого, есть три составных компонента, важных в сервис-ориентированной архитектуре: клиентское приложение, которое позволяет запускать бизнес-процессы и смотреть их результаты; сервис репозиторий — хранилище описания услуг в сервис-ориентированной системе, которое будет помогать не только их обнаруживать, но и предоставлять дополнительную информацию об их состоянии/функциональности; шина данных — кровеносная артерия всей архитектуры. Рассмотрим это на простом примере: Здесь есть самая главная часть — Enterprise Service Bus (шина данных). К ней подключены различные сервисы, общающиеся между собой только через шину данных с помощью сообщений. У сервиса могут быть свои базы данных, которые могут быть без всякого хранения. К шине могут подключаться различные клиентские приложения. Самое главное, что все сервисы должны осуществлять коммуникацию с помощью шины данных. Это нам позволяет достичь невероятной гибкости, потому что мы можем собирать нашу информационную систему, как конструктор, добавляя или убирая из бизнес-процессов бизнес-функции, услуги, сервисы, тем самым, меняя поведение системы. Данный подход дает нам небывалое масштабирование. Главное, чтобы шина данных выдерживала возложенные на нее нагрузки. Шина дает нам гибкость, так как система не зависит ни от какого языка программирования или технологии. Наши услуги мы можем разрабатывать на любых технологиях и языках программирования. Главное, договориться о контрактах и их соблюдать (о форматах сообщений, как они обрабатываются и т.д.). У данной системы есть минус, который заключается в сложности. Необходимо продумать, как будет выглядеть сквозной бизнес-процесс. Сложно спроектировать большой бизнес-процесс, в котором будут участвовать много услуг, бизнес-функций, затронутых в этой системе. Некоторые будут синхронно отвечать, некоторые — асинхронно, какие-то будут падать с ошибками. Нагрузка на системных аналитиков возрастает в разы. Здесь есть сложность сопровождения шины данных и всех услуг для того, чтобы справиться с хаосом, когда в эту шину данных будут слишком много писать или в ней вычитывать, тем самым данные будут теряться, а очередь — переполняться. Самое главное — это не выбор сервис-ориентированной архитектуры, а просмотр проблемы, которую она решает и какими способами она это делает. Использование шины данных в других архитектурах вам даст такие же плюсы, как сервис-ориентированная. Вы будете лишены недостатков, если будете реализовывать всю архитектуру по канонам сервис-ориентированной архитектуры. Плюсы и минусы Плюсы: Гибкость; Масштабирование. Минусы: Сложность. Микросервисы Микросервисы — современное и распространенное понятие. Сервис-ориентированная архитектура была придумана как способ интегрировать различные направления, бизнес-функции внутри предприятия. Микросервисы выполняет практически ту же функцию и роль. Они позволяют декомпозировать логику в одном приложении, разбив их на микрослужбы/микрофункции, которые позволяют выполнять ту или иную задачу внутри большой системы. Интегрируя сервисы друг с другом, можно достигать поставленной перед системой цели. Если посмотреть на технические реализации этих двух подходов, то в сервис-ориентированной архитектуре участвует понятие «монструозная» шина данных, в микросервисах же благодаря развитию технологий, популяризации других стандартов общения и сетевых взаимодействий, используются легковесные, сетевые протоколы (например, протобаф, трифт, json и т.д.) в отличие от шины данных, которая была практически единственным источником связи в сервис-ориентированной архитектуре. Однако микросервисы обычно сравниваются с монолитом, как хорошее против плохого, но на самом деле, это всего лишь способ декомпозиции логики или функционала внутри большого приложения или сервиса. Сделав плохой монолит, вы можете сделать и плохую микросервисную архитектуру. В этом вас тоже никто не ограничивает. Например, вас никто не ограничивает сделать связь и еще одну кольцевую связь. В последствии это превратится в большой «ком грязи», который неизвестно, как работает. Это лишит нашу архитектуру слабых связанностей и высокой связанности, того, к чему необходимо стремиться. В первую очередь хорошая микросервисная архитектура должна отвечать этим понятиям. Когда мы выделяем наш микросервис, необходимо, чтобы он отвечал за свою бизнес-функциональность, имел простой интерфейс и сам бы распоряжался своими данными. Поэтому в микросервисных подходах часто делают так, чтобы каждый микросервис имел свою базу данных. Это дает нам плюс в том, что мы имеем гибкость, так как можем выбирать технологии, удовлетворяющие конкретные задачи, возлагаемые на определенные микросервисы. Однако наряду с гибкостью возникает проблема сложности. Все, кто работал с большими микросервисными архитектурами, знают, что нужно вокруг них выстраивать значительную часть инструментов для возможности ими управлять. Например, если в монолите есть приложение, которое пишет log истории операций действий, то мы просто забираем этот log и читаем, то в микросервисе уже невозможно просто читать log. Для этого необходимо будет сооружать еще одну инфраструктуру сбора log. Если в монолите мы можем легко продебажить, то в микросервисах так сделать не получится. Например, в один сервис приходит запрос, он порождает один вызов, тот следующий и т.д., мы не можем перемешаться по IDE. Придется оборачивать это в другие системы, позволяющие делать распределенный трейсинг. Если у нас много сервисов, и на них приходится большая нагрузка, то все вспомогательные сервисы логирования, трейсинга тоже будут иметь большую нагрузку. Плюсы и минусы Плюсы Гибкость; Масштабирование. Минусы Сложность. Если у нас небольшая система, которую можно быстро создать, можно выбрать монолит. Если система будет эволюционно развиваться, то кроме микросервисов из остальных архитектур выбрать будет нечего. Выбирайте архитектуру под задачу, которую вам нужно решить. Не стремитесь к мысли о том, что микросервис — лучшая архитектура из доступных. Каждый архитектурный подход необходимо выбирать по уместности и пользе. CQRS, Event Sourcing Обычно CQRS идет рука об руку с Event Sourcing. Мы в нашей предметной области, архитектуре и бизнес-цели нашей системы, ключевую роль назначаем событиям. События — изменения в вашей системе, например, создался какой-то объект или удалился. Если мы не будем сразу отражать изменение в базе данных как единственные источники правды, а будем сохранять как само изменение/инкремент, то мы получим определенные плюсы. Храня log таких событий, мы можем не сохранять и не следить за консистентностью базы данных, а следить за консистентностью log событий. Мы будем иметь аудит всех операций в нашей системе (в некоторых операциях это бизнес-критично), знать, что и когда менялось в системе, а также, следя за консистентностью log события, можно гарантировать получение слепка базы данных в любое время, применив события с последней контрольной точки по базе данных для чтения. Рассмотрим данную архитектуру. Данная архитектура делится на две части: Часть, отвечающую за хранение команд. Здесь у нас будет Event Store, которая сохраняет весь набор событий в системе. Если у нас возникают какие-то изменения в системе, то они должны идти через команды, а команды записываться в Event Store. Здесь же они зеркалируются в подсистему для чтения. В подсистеме у нас тоже должна быть своя база данных, которая будет хранить модели для чтения, и все события команды, которые происходят в верхней подсистеме, должны зеркалироваться и накладываться на базу данных. У этой системы достаточно много плюсов. Например, аудит операций. Нам надо будет следить только за консистентностью базы данных для записи. Мы можем откатываться в прошлое, чтобы понять, как выглядела база данных для чтения. Здесь будут присутствовать и проблемы. Например, будет сложно держать в голове и проектировать две системы: работающую на записи и на чтении. В этой системе не получится достигнуть полной консистентности. У нас будет Eventual Consistency. Когда у нас произошла команда, мы не сразу будем читать измененные значения после транзакции, это значит, что мы получим данные, когда команда пройдет по базе данных для чтения. Некоторые системы, с помощью которых можно было вернуться в прошлое, имеют жесткий аудит событий, который будет происходить в системе. CQRS и Event Sourcing практически единственный вариант, где это можно понятно сделать.