See More

[Вопросы для собеседования](README.md) # ООП + Микросервисы + [Что такое _ООП_?](#Что-такое-ООП) + [Назовите основные принципы _ООП_.](#Назовите-основные-принципы-ООП) + [Что такое _«инкапсуляция»_?](#Что-такое-инкапсуляция) + [Что такое _«наследование»_?](#Что-такое-наследование) + [Что такое _«полиморфизм»_?](#Что-такое-полиморфизм) + [Что такое _«абстракция»_?](#Что-такое-абстракция) + [Что представляет собой _«обмен сообщениями»_?](#Что-представляет-собой-обмен-сообщениями) + [Расскажите про основные понятия ООП: _«класс»_, _«объект»_, _«интерфейс»_.](#Расскажите-про-основные-понятия-ООП-класс-объект-интерфейс) + [В чем заключаются преимущества и недостатки объектно-ориентированного подхода в программировании?](#В-чем-заключаются-преимущества-и-недостатки-объектно-ориентированного-подхода-в-программировании) + [Что подразумевают в плане принципов ООП выражения _«является»_ и _«имеет»_?](#Что-подразумевают-в-плане-принципов-ООП-выражения-является-и-имеет) + [В чем разница между _композицией_ и _агрегацией_?](#В-чем-разница-между-композицией-и-агрегацией) + [Что такое _статическое_ и _динамическое связывание_?](#Что-такое-статическое-и-динамическое-связывание) + [Принцип SOLID?](#Принцип-SOLID) + [Что такое Монолит, Микросервис?](#Что-такое-Монолит,-Микросервис) + [Что такое Continuous Integration, Continuous Delivery/Deployment?](#Что-такое-Continuous-Integration,-Continuous-Delivery/Deployment) + [Что выбрать Монолит или Микросервис?](#Что-выбрать-Монолит-или-Микросервис) + [Взаимодействие между микросервисами](#Взаимодействие-между-микросервисами) + [OpenAPI](#OpenAPI) + [Тестирование микросервисов](#Тестирование-микросервисов) + [Распределенная трассировка](#Распределенная-трассировка) ## Что такое _ООП_? __Объектно-ориентированное программирование (ООП)__ — методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования. + объектно-ориентированное программирование использует в качестве основных логических конструктивных элементов объекты, а не алгоритмы; + каждый объект является экземпляром определенного класса + классы образуют иерархии. Программа считается объектно-ориентированной, только если выполнены все три указанных требования. В частности, программирование, не использующее наследование, называется не объектно-ориентированным, а программированием с помощью абстрактных типов данных. Согласно парадигме ООП программа состоит из объектов, обменивающихся сообщениями. Объекты могут обладать состоянием, единственный способ изменить состояние объекта - послать ему сообщение, в ответ на которое, объект может изменить собственное состояние. [к оглавлению](#ООП) ## Назовите основные принципы _ООП_. + _Инкапсуляция_ - сокрытие реализации. + _Наследование_ - создание новой сущности на базе уже существующей. + _Полиморфизм_ - возможность иметь разные формы для одной и той же сущности. + _Абстракция_ - набор общих характеристик. + _Пересылка сообщений_ - форма связи, взаимодействия между сущностями. + _Переиспользование_- все что перечислено выше работает на повторное использование кода. Это единственно верный порядок парадигм ООП, так как каждая последующая использует предыдущие. [к оглавлению](#ООП) ## Что такое _«инкапсуляция»_? __Инкапсуляция__ – это свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе и скрыть детали реализации от пользователя, открыв только то, что необходимо при последующем использовании. Цель инкапсуляции — уйти от зависимости внешнего интерфейса класса (то, что могут использовать другие классы) от реализации. Чтобы малейшее изменение в классе не влекло за собой изменение внешнего поведения класса. [к оглавлению](#ООП) ## Что такое _«наследование»_? __Наследование__ – это свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется _предком_, _базовым_ или _родительским_. Новый класс – _потомком_, _наследником_ или _производным_ классом. [к оглавлению](#ООП) ## Что такое _«полиморфизм»_? __Полиморфизм__ – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. Т.е. использовать разные объекты одинаково, через общий интерфейс. Пример: у нас есть метод draw(). Для круга он рисует окружность, для квадрата — квадрат. Но мы обращаемся к ним одинаково: figure.draw(). Преимуществом полиморфизма является то, что он помогает снижать сложность программ, разрешая использование одного и того же интерфейса для задания единого набора действий. Выбор же конкретного действия, в зависимости от ситуации, возлагается на компилятор языка программирования. Отсюда следует ключевая особенность полиморфизма - использование объекта производного класса, вместо объекта базового (потомки могут изменять родительское поведение, даже если обращение к ним будет производиться по ссылке родительского типа). Полиморфизм бывает _динамическим_ (переопределение методов в потомках) и _статическим_ (перегрузка методов -одинаковое имя, разные параметры). _Полиморфная переменная_, это переменная, которая может принимать значения разных типов, а _полиморфная функция_, это функция у которой хотя бы один аргумент является полиморфной переменной. Выделяют два вида полиморфных функций: + _ad hoc_, функция ведет себя по разному для разных типов аргументов (например, функция `draw()` — рисует по разному фигуры разных типов); + _параметрический_, функция ведет себя одинаково для аргументов разных типов (например, функция `add()` — одинаково кладет в контейнер элементы разных типов). [к оглавлению](#ООП) ## Что такое _«абстракция»_? _Абстрагирование_ – это способ выделить набор общих характеристик объекта, исключая из рассмотрения частные и незначимые. Соответственно, __абстракция__ – это набор всех таких характеристик. Пример: когда мы говорим «машина», нам важно, что у неё есть двигатель и колёса, а цвет ковриков внутри — неважно. [к оглавлению](#ООП) ## Что представляет собой _«обмен сообщениями»_? Объекты взаимодействуют, посылая и получая сообщения. Сообщение — это запрос на выполнение действия + данные. В ООП посылка сообщения (вызов метода) — это единственный путь передать управление объекту. Если объект должен «отвечать» на это сообщение, то у него должна иметься соответствующий данному сообщению метод. Так же объекты, используя свои методы, могут и сами посылать сообщения другим объектам. [к оглавлению](#ООП) ## Расскажите про основные понятия ООП: _«класс»_, _«объект»_, _«интерфейс»_. __Класс__ – шаблон описания сущности, определяющий состояние и поведение, зависящее от этого состояния, а также правила для взаимодействия с данной сущностью (контракт). С точки зрения программирования класс можно рассматривать как набор данных (полей, атрибутов, членов класса) и функций для работы с ними (методов). С точки зрения структуры программы, класс является сложным типом данных. __Объект (экземпляр)__ – это отдельный представитель класса, имеющий конкретное состояние и поведение, полностью определяемое классом. Каждый объект имеет конкретные значения атрибутов и методы, работающие с этими значениями на основе правил, заданных в классе. __Интерфейс__ – это набор методов класса, доступных для использования. Интерфейсом класса будет являться набор всех его публичных методов в совокупности с набором публичных атрибутов. По сути, интерфейс специфицирует класс, чётко определяя все возможные действия над ним. [к оглавлению](#ООП) ## В чем заключаются преимущества и недостатки объектно-ориентированного подхода в программировании? ✅ Ближе к человеческому восприятию мира. ✅ Код разбивается на логичные части. ✅ Удобнее поддерживать и расширять. ✅ Код можно переиспользовать. ✅ Легко добавлять новые объекты и изменять поведение программы. ❌ Иногда сложно понять, где что наследуется. ❌ Методы могут быть «размазаны» по разным классам. ❌ Документировать сложнее. Поскольку любой метод может быть переопределен, в документации должно говориться не только о том, что делает данный метод, но и о том, в каком контексте он вызывается. ❌ Дополнительные расходы памяти и производительности. ❌ Избыточность. Часто содержится больше методов, чем это реально необходимо текущей программе. А поскольку лишние методы не могут быть удалены, они становятся мертвым грузом. [к оглавлению](#ООП) ## Что подразумевают в плане принципов ООП выражения _«является»_ и _«имеет»_? __«является»__ подразумевает наследование. Класс _cat_ является потомком класса _animal_ __«имеет»__ агрегация или композиция. Класс _car_ имеет класс _engine_ [к оглавлению](#ООП) ## В чем разница между _композицией_ и _агрегацией_? Ассоциация обозначает связь между объектами. Агрегация — «часть» может существовать отдельно и принадлежать нескольким «целым». (Книга находится в библиотеке, её можно перенести в другую). Композиция — «часть» жёстко привязана к «целому». (Страница принадлежит конкретной книге). [к оглавлению](#ООП) ## Что такое _статическое_ и _динамическое связывание_? Связывание означает наличие связи между ссылкой и кодом. Например, переменная, на которую вы ссылаетесь, привязана к коду, в котором она определена. Аналогично, вызываемый метод привязан к месту в коде, где он определен. _Статическим_ или _ранним связыванием (early binding)_ — компилятор заранее знает, какой метод будет вызван. (Напр. перегрузка методов). _Позднее связывание (late binding) или динамическое_ проводимое непосредственно во время выполнения программы, в зависимости от типа объекта (Напр. переопределение методов в наследниках). Механизм позднего связывания зависит от конкретного языка Итак, фундаментальное различие между статическим и динамическим связыванием в Java состоит в том, что первое происходит рано, во время компиляции на основе типа ссылочной переменной, а второе – позднее, во время выполнения, с использованием конкретных объектов. В Java почти все методы — динамические, кроме `static`, `final` и `private`. Ключевые различия между ранним и поздним связыванием в языке Java: 1) Статическое связывание используется в языке Java для разрешения перегруженных методов, в то время как динамическое связывание используется в языке Java для разрешения переопределенных методов. 2) Аналогично, приватные, статические и терминальные методы разрешаются при помощи статического связывания, поскольку их нельзя переопределять, а все виртуальные методы разрешаются при помощи динамического связывания. 3) В случае статического связывания используются не конкретные объекты, а информация о типе, то есть для обнаружения нужного метода используется тип ссылочной переменной. С другой стороны, при динамическом связывании для нахождения нужного метода в Java используется конкретный объект. [к оглавлению](#ООП) ## Принцип SOLID? Пять основных принципов дизайна классов: + Single Responsibility Principle (Принцип единственной ответственности). + Open Closed Principle (Принцип открытости/закрытости). + Liskov’s Substitution Principle (Принцип подстановки Барбары Лисков). + Interface Segregation Principle (Принцип разделения интерфейса). + Dependency Inversion Principle (Принцип инверсии зависимостей). __Принцип единственной ответственности (SRP)__
Никогда не должно быть больше одной причины изменить класс. Класс должен быть ответственен лишь за что-то одно. Если класс отвечает за решение нескольких задач, его подсистемы, реализующие решение этих задач, оказываются связанными друг с другом. Изменения в одной такой подсистеме ведут к изменениям в другой. Представьте себе модуль, который обрабатывает заказы. Если заказ верно сформирован, он сохраняет его в базу данных и высылает письмо для подтверждения заказа. Принцип единственной обязанности подразумевает, что три аспекта этой проблемы на самом деле — три разные обязанности. А значит, должны находиться в разных классах или модулях. Объединение нескольких сущностей, которые могут меняться в разное время и по разным причинам, считается плохим проектным решением. Гораздо лучше разделить модуль на три отдельных, каждый из которых будет выполнять одну единственную функцию. __Принцип открытости/закрытости (OCP)__
Программные сущности (классы, модули, функции и т.п.) должны быть открыты для расширения, но закрыты для изменения. Это означает, что должна быть возможность изменять внешнее поведение класса, не внося физические изменения в сам класс. Следуя этому принципу, классы разрабатываются так, чтобы для подстройки класса к конкретным условиям применения было достаточно расширить его и переопределить некоторые функции. Поэтому система должна быть гибкой, с возможностью работы в переменных условиях без изменения исходного кода. Используются интерфейсы __Принцип подстановки Барбары Лисков (LSP)__
Объекты в программе можно заменить их наследниками без изменения свойств программы. Это означает, что класс, разработанный путем расширения на основании базового класса, должен переопределять его методы так, чтобы не нарушалась функциональность с точки зрения клиента. То есть, если разработчик расширяет ваш класс и использует его в приложении, он не должен изменять ожидаемое поведение переопределенных методов. Если оказывается, что в коде проверяется тип класса, значит принцип подстановки нарушается. __Принцип разделения интерфейса (ISP)__
Клиенты не должны быть вынуждены реализовывать методы, которые они не будут использовать. Принцип разделения интерфейсов говорит о том, что слишком «толстые» интерфейсы необходимо разделять на более мелкие и специфические, чтобы клиенты мелких интерфейсов знали только о методах, необходимых в работе. Рассмотрим пример. Разработчик Алекс создал интерфейс "отчет" и добавил два метода: generateExcel() и generatedPdf(). Теперь клиент А хочет использовать этот интерфейс, но он намерен использовать отчеты только в PDF-формате, а не в Excel. Он должен будет реализовать два метода, один из которых по большому счету не нужен и существует только благодаря Алексу — дизайнеру программного обеспечения. Клиент воспользуется либо другим интерфейсом, либо оставит поле для Excel пустым. Так в чем же решение? Оно состоит в разделении существующего интерфейса на два более мелких. Один — отчет в формате PDF, второй — отчет в формате Excel. Это даст пользователю возможность использовать только необходимый для него функционал. __Принцип инверсии зависимостей (DIP)__
Зависимости внутри системы строятся на основе абстракций. Объектом зависимости должна быть абстракция, а не что-то конкретное. Модули верхнего уровня не зависят от модулей нижнего уровня. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. Программное обеспечение нужно разрабатывать так, чтобы различные модули были автономными и соединялись друг с другом с помощью абстракции. Классическое применение этого принципа — Spring framework. В рамках Spring framework все модули выполнены в виде отдельных компонентов, которые могут работать вместе. Они настолько автономны, что могут быть быть с такой же легкостью задействованы в других программных модулях помимо Spring framework. Это достигнуто за счет зависимости закрытых и открытых принципов. Все модули предоставляют доступ только к абстракции, которая может использоваться в другом модуле. [к оглавлению](#ООП) ## Что такое Монолит, Микросервис? Монолитная архитектура - система с одним сервисом, который отвечает за работу со всей предметной областью бизнеса (приложения) При начале работы над монолитным проектом Достоинства: ✅ Быстрая разработка ✅ Возможность внесения радикальных изменений (например изменить архитетуру, класс-дизайн, схему бд) ✅ Безболезненое тестирование ✅ Легкое развертывание ✅ Простое hardware масштабирование С увеличением размера проекта и команды разработки появляются Минусы: ❌ Медленное внесение изменений Команды А и Б работают не пересекаясь. Выкатывается новая версия проекта с багами в коде команды Б. Откатывается на предыдущуюю версию весь код и команды Б и команды А. ❌ Уязвимая надежность - если падает часть кода, то не работает все приложение. ❌ Трудности в hardware масштабировании. Одна часть требует оперативу, другая ядра. Приходится апгрейдить "вертикально" - покупать больше оперативных планок и процессоры с большим кол-вом ядер для всех сервисов. А не по потребностям ❌ Дороговизна обновления технологического стека. Например переход на новую версию Java. Чтобы проверить эффективность, нужно переписывать весь проект Микросервисная архитектура — система, которая содержит больше одного обособленнго сервиса. Размер значения не имеет. Обособленный сервис — является независимой единицей развертывания, имеет своё хранилище, свою БД (?), отвечает за свою часть предметной области Достоинства: ✅ Возможность масштабирования разработки ✅ Дешевизна эксперементов с новым стеком ✅ Инкрементальное обновление технолошического стека ✅ Повышенная надежность и отказоустойчивость Минусы: ❌ Сложность проектирования архитектуры ❌ Долгое внесение радикальных изменений ❌ Нетривиальное тестирование ❌ Повышенные требования к Continuous Integration/Continuous Delivery (деплою) Сложности проектирования Микросервисов + Сложно разделить предметную область на части Не стоит делить монолит на микросервисы по функциональным возможностям. Это приведет к распределенному монолиту - архитектура, включающая недостатки и того и другого. Монолит нужно разбивать по пренадлежности к какой-то области бизнеса. Отличить одно от другого довольно сложно. Т.е. по принципу единой ответственности. Соответствие принципам Low coupling, high cohesion способствует быстрому внесению изменений и легкому тетсированию. Сильная связанность high cohesion - части системы, которые изменяются вместе, должны находиться ближе друг к другу Слабая связанность low coupling - части системы, которые изменяются параллельно, должны иметь как можно меньше зависимостей друг на друга b внутри сервиса и между сервисами ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP1.png) + Задержки при межсервисном взаимодействии влияют на производительность + сеть ненадежна + Межсервисное взаимодействие влияет на доступность + Сложно добиться согласованности данных [к оглавлению](#ООП) ## Что такое Continuous Integration, Continuous Delivery/Deployment? Непрерывная интеграция (CI) — процесс автоматического принятия изменений. Первичный процесс обновления ПО, в рамках которого все изменения на уровне кода вносятся в единый центральный репозиторий. Такое внесение принято называть слиянием. После каждого слияния (которое проходит по несколько раз в день) в изменяемой системе происходит автоматическая сборка (часто приложение упаковывается в Docker) и тестирование (проверка конкретных модулей кода, UI, производительности, надёжности API). Таким образом разработчики страхуются от слишком поздних обнаружений проблем в обновлениях. + Возможность локальной сборки проекта + На каждый коммит в кдаленный репо запускаем сборки проекта в системе (Jenkins, TravisCI) + Сломанная сборка запрещает слияние Непрерывная доставка (CD) — CI + CD. Автоматизированый процесс доставки изменений до продакшена. Теперь новая версия не только создаётся и тестируется при каждом изменении кода, регистрируемом в репозитории, но и может быть оперативно запущена по одному нажатию кнопки развёртывания. Однако запуск развёртывания всё ещё происходит вручную — ту самую кнопку всё же надо кому-то нажать. Этот метод позволяет выпускать изменения небольшими партиями, которые легко изменить или устранить в случае необходимости. + Поддерживаем артефакты стабильными + Развертываем по кнопке [к оглавлению](#ООП) ## Что выбрать Монолит или Микросервис? Микро если: + Предметная область хорошо делится на части + Много направлений, которые нужно развивать параллельно + "Особенности" монолита не устраивают бизнес Монолит если: + Нужно быстро сделать прототип + Вашу задачу можно решить одним сервисом + Непонятно, как разделить предметную область + Низкие нагрузки, мало разработчиков, нет ресурсов на поддержание микросервисов https://microservices.io/ https://martinfowler.com/ "microservices patterns" chris richardson "clean architecture" robert martin [к оглавлению](#ООП) ## Взаимодействие между микросервисами Межсервисное взаимодействие = сетевое взаимодействие, т.е. система управляется сообщениями + Производительность = задержки и пропускная способность + Доступность и отказоустойчивость + Архитектура __Транспорт__ + стандарт JSON внутри HTTP (REST, RPC, GraphQL). Простой, множество инструментов, человекочитаемость. Из-за текстового формата большие объемы и нужно оптимизировать + бинарные протоколы: gRPC, Thrift, Avro и т.д. Выше производительность, оптимизированно представление данных, можно определить схему сообщения из коробки, но меньше инструментов и не читаемы человеком Что выбрать? + Начинать с HTTP + Бинарные не бесплатные + Большинство проблем не из-за протокола! + Для публичных API всегда HTTP либо несколько реализаций одна из которых HTTP __API__ Application Programming Interface - интерфейс, определяющий способ и схему взаимодействия с сервисом + Remote Procedure Call (RPC) Только POST метод. Все параметры в теле запроса. Любой статус !=200, считается ошибкой Выбирают: Простая реализация, Внутренние API, HTTP только как транспорт + Representational State Transfer (REST) Архитектурный стиль, в основе которого лежат ресурсы и их идентификация, изменение их состояния через представление. REST != HTTP Выбирают: Своего рода стандарт, Внутренние API, Публичные API, HTTP как протокол + GraphQL Язык запросов для API. Например можно задать конкретный набор полей, которые хотим получить в ответе на запрос Выбирают: Типо-безопасность, API для Web и мобильных клиентов, тут свой инструментарий __Проектирвоание__ + Клиент-сервер + Stateless - сервис не должен хранить состояние в памяти, это важно для масштабирования + Кеширование __Типы сообщений__ + Команды + Императивное управление (звучит как "сделать что-то"), вызывают сильную связанность, зато понятны - названия обычно отражают логику ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP2.png) + События + Декларативное управление (звучит как "что-то произошло"), снижают связанность, менее понятны - не знаем что скрыто в notifyOrderCreated ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP3.png) __Виды взаимодействия__ + Синхронные - отправляем запрос и блокируемся, ожидая результата. Просто и понятно, быстрые операции и низкие задержки (ок), низкая пропускная способность (не ок), вызывает низкую доступность (не ок) + Ассинхронные - не блокируемся ожидая ответ Сложнее в реализации, выше задержки (не ок), выше пропускная способность (ок), повышает доступность (ок) Short polling - вызываем операцию и получаем ссылку на ресурс с результатом операции. Переодически опрашиваем этот ресурс на предмет текущего статуса операции ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP4.png) Пользователь должен ждать как можно меньше, и нужно выполнять как можно меньше запросов на опрос Стратегии опроса: + Фиксированное время (раз в секунду) - просто, но время запроса может чуть отличаться, что приведет к лишним запросам + Прогрессия (геометрическая/алгебраическая) - сокращает кол-во запросов, но возможен вариант когда придется долго ждать + На основе статистического распределения - например, знаем, что запрос выполняется 50% за 1.5с, 90% за 3с и 99% за 5с, мы можем распределить запросы во времени Callbacks - вызываем операцию и получаем идентификатор операции. Сервис, выполняющий операцию, отправляет уведомление вызвавшему сервису о результате выплонения операции ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP5.png) __Способы взаимодействия__ + Прямой обмен А->Б - просто, легкая эксплуатация, но сильная связанность сервисов. Хорошо для Команд + Обмен через брокеры сообщений А->Брокер->Б - сложнее, позволяет снизить связанность сервисов. Хорошо для Событий На основе callback: RabbitMQ ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP6.png) На основе polling: Kafka ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP7.png) Микросервисы могут работать одновеременно с разными диалектами БД. При этом есть __сильная согласованность__, когда изменения сразу видны всем, с момента их применения и __конечная согласованность__ когда изменения будут видно, но не сразу, а доставляются асинхронно __Сильная согласованность__ - всегда актуальные данные, но высокие задержки при получении В таких случаях используются __Распределенные транзакции__. Но это сложно, дорого и есть не везде. Можно эмулировать распределенную транзакцию с помощью вложенных локальных транзакций ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP8.png) При этом возврат ошибки не гарантирует, что запись не была вставленна в БД. А откат первой транзакции не приводит к откату второй. Saga паттерн: Для решения нужно разбивать одну большую распределенную транзакцию на последовательность локальных транзакций и предоставить возможность отката всех локальных транзакций. ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP9.png) Особенности: нужно уметь восстанавливаться после сбоев любого участка и повторять сагу с определенного шага + отличать финальные ошибки от нефинальных Хореография ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP10.png) + Нет единой точки отказа + Легко менять кусочек процесса + Сложно понять процесс целиком и отслеживать ошибки Оркестрация ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP11.png) + Есть единая точка отказа + Сложнее вносить изменения в места + Легко понять процесс и отслеживать ошибки ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP12.png) __Конечная согласованность__ - могут быть неактуальные данные, зато низкие задержки ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP13.png) __Взаимоисключения__ Иногда требуется выполнять какой-то процесс эксклюзивно. Например используя распределенный консенсус - алгоритмы, которые сложно и медленно работают. etcd, Consul, Zookeeper. Зато отказоустойчивы Альтернатива - red lock, когда БД используется в качестве хранилища блокировок. Блокировка не захватывается, а арендуется на время. Чтобы в случае блокировки и отказа системы блокировка не осталась навсегда Добавляем версию блокировки для контроля ее корректности ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP14.png) [к оглавлению](#ООП) ## OpenAPI OpenAPI - спецификация для описания REST API. https://habr.com/ru/post/541592/ Swagger - это фреймворк для спецификации RESTful API, цель которого - поддерживать документацию в актуальном виде. Его прелесть заключается в том, что он дает возможность не только интерактивно просматривать спецификацию, но и отправлять запросы – так называемый Swagger UI. + Code first - сначала пишем код, потом генерируем спецификацию + Schema first - сперва спецификация, затем генерируем по ней код + Публикуем спецификацию (отделный артефак или Swagger) + Строго следовать этой спецификации Документирование БД Database Schema as a Code - текущая схема должна быть представлена последовательностью патчей, хранимых как код + Возможность восстановить БД на любом окружении + Аудит, контроль, валидация + Flyway, Liquibase [к оглавлению](#ООП) ## Тестирование микросервисов + Компонентные тесты проверяют работу сервисов и их взаимодействие + Модульные тесты для тестирования отдельных функций Компонентных должно быть больше, чем модульных, т.к. изменяемая среда, много зависимочти между классами Тесты должны быть независимыми и уметь работать параллельно. Для тестирования API лучше делать реальные HTTP вызовы, валидировать ответ согласно схеме (JSON). RESTAssured как пример инструмента. При тестирование взаимодействия с БД на каждый запуск тестов локально поднимается БД. Embedded или TestContainers. Тесты не должны очищать БД или буффер брокера сообщений перед/после своего запуска/завершения При тестировании взаимодействия микросервисов на каждый запуск тестов локально HTTP-сервер, создаем моки операций сервисов, выполняем, моки валидируем. WireMock, MockServer При тестировании аинхронных процессов стараемся приблизить тестовый сценарий к реальному, запускаем операцию и ждем завершения выполнения. Awaitility Для автоматизации развиваем Continuous Integration/Continuous Delivery - помогает быстрее развивать сервисы и доставлять их на production. Но дорого. [к оглавлению](#ООП) ## Распределенная трассировка При межсервисном взаимодействии сложно отследить весь путь запроса по системе + К каждому внешнему запрсос прикрепляем "идентификатор операции" + Пробрасываем его по всей системе + Добавляем его в логовые сообщения для отслеживания. Elastic APM, OpenTracing Для HTTP: Прикрепляем HTTP-заголовок с этим идентификатором - Используем его при обработке запроса - Поллинг делаем с тем же индентификаторо - Обраный вызов с ним же - В брокер сообщений также отправляем этот индентификатор Для Саги: Исходный запрос теряется при обработке асинхронных процессов - В задачу на продолжение саги кладем исходный идентификатор операции (в БД) - При возобновлении саги используем этот идентификатор Визуализация трасс помогает построить схему взаимодействия между сервисами. Можно это делать атоматически и динамически. Zipkin, Jaeger [к оглавлению](#ООП) # Источники + [DevColibri](http://devcolibri.com/720) + [Хабрахабр](https://habrahabr.ru/post/87119/) + [Википедия](https://ru.wikipedia.org/wiki/Объектно-ориентированное_программирование) [Вопросы для собеседования](README.md)