Техническая база для менеджеров продукта — часть 5 — архитектура продукта
Записи из курса по technical product manager; позиционируют как: “Говори с разработчиками на одном языка”.
Это 5я часть из серии, в ней собраны: подходы в программировии (ООП, функциональное, процедурное), (много)поточность и (а)синхронность, монолит vs микросервисы, масштабируемость систем, паттерны проектирования (зачем это менеджеру?), хорошие практики (SOLID).
Подходы в программировании
ООП
Парадигма (свод правил и рекомендаций) программирования, рекомендует “в разработке — используйте объекты; всё объект”, которая основана на принципах (не обязательно все):
-
инкапсуляция — совокупность методов и свойств, которые находятся внутри объекта; есть объект, у него есть интерфейсы (для управления, взаимодействия), нет необходимости лезть внутрь (вникать во внутр. процессы); объект дается как есть (готов и использованию);
Например: автомобиль; все +- знают что это, но мало кто понимает детальное устройство внутри, работу механизмов и тд; но это не мешает людям ездить, управлять и тд.
В разработке: я могу брать наработки других разработчиков (объекты), даже не до конца понимая полноту реализации; -
наследование — способность объектов, порождать себе подобные, с наследованием свойств предка; объект наследует родительский класс и может его расширять (добавлять свойства);
Например: на дефолтный автомобиль добавили ГБО, багажник на крышу, прицеп и коврики в салон; это всё тот же автомобиль (он едет и имеет те дже функции), но с дополнениями; -
полиморфизм — все схожие объекты, могут реализовывать методы разными способами; способность обьекта использовать методы производного класса, который не существует на момент создания базового (родителя);
Например: расширяем средство передвижения от автомобиля, до велосипеда, трактора, самоката, мотоцикл — любое средство с колесами; -
абстракция*—объекты, которые реализуют нужные методы, разными способами;
Например: нам нужно нечто, что двигается на колёсах; в итоге, это может быть любой транспорт, от гироскутера до поезда.
Это лучший вариант для продуктовой разработки.
Процедурное программирование
Альтернатива ООП. Вместо объектов, используем функции, которые являются подпрограммами (содержит алгоритм, объекты внутри).
Программирование, при котором последовательно выполняемые команды, можно собрать в подпрограммы (более крупные, целостные единицы кода).
Особенности:
- Функции являются подпрограммами;
- Алгоритмическое выполнение (шаг за шагом);
- Чрезмерная логика в одном месте (в рамках функций);
- Трудно поддерживать и дорабатывать.
Функциональное программирование
Это стиль программирования, а не подход. В нём нет переменных, как таковых (не сохраняет промежуточные результаты), нельзя вернуть результат работы в процессе (вывести дебаг, посчитать что-то и обратится к нему, работа с переменными).
Особенности:
- Функции высших порядков (функции, могут принимать на вход, как аргументы, другие функции);
- Чистые функции;
- Неизменяемость (результат работы каждой функции, будет одним и тем же значениям; нет неожиданных результатов);
- Возможности параллелизма (
- Требует особых подходов при разработке (но, тяжело найти реальную продуктовую задачу под эту технология; академическая тема);
Пример: язык Haskell
Многопоточность vs Асинхронность
Поточность — это про процесс; бывает одно- и много- поточные;
один поток — один процес в единицу времени (без переключения, ожна задача от начала до концеа);
Асинхронность — это модель обработки данных; может быть синхрон. и асинхрон.
асинхронность — возможность переключения между задачами (данными) в процессе работы (дошёл до блокера — начал делать блокер — сделал — вернулся к первой);
Как это работате вместе? Сценарии: все пересечения процесса и модели.
Сценарии
Например: все современные компьютеры по-дефолту работают в асинхронном многопоточном сценарии; при этом, некоторые программы могут работать иначе: очередь — это однопоточное действие; но, если запустить несколько демонов, они могут многопоточно и синхронно разгребать её.
Пример от Valeriy Gello:
Параллельно — у вас 2 мясорубки, которые параллельно (одновременно и независимо) делают фарш.
Асинхронно — у вас 2 повара, которые одновременно готовят на одной кухне. Да, первый повар может дожарить котлеты второго, но так же первый может делать салат, а второй борщ. И на выходе у нас будет 2 блюда, приготовленных параллельно. Первый повар может пойти покурить, второй от этого не остановится. Они не синхронно работают, а асинхронно (независимо, хотя и могут делиться работой).
И чтобы окончательно раскрыть тему — эффективнее всего работать конкурентно. Когда оба повара параллельно берут атомарные задачи из очереди по мере выполнения или блокировки предидущих.
Архитектура продукта
На уровне приложения; это подход, для реализации задач в рамках приложения. Это шаблоны, которые могут лежать в основе архитектуры:
- Многоуровневый шаблон
- Клиент-серверный шаблон (CS
- Ведущий-ведомый
- Каналы и фильтры
- Шаблон посредника
- Одноранговый шаблон
- Шина событий
- Модель-представление-контроллер (MVC)
- Доска
- Интерпретатор
Монолит и микросервисы
Это верхний (самый доступный) способ описать архитектуру системы, для постороннего человека.
Монолит — весь backend (и мб frontend) это единое приложение: одна БД, одна точка входа;
Характеристики монолита:
- Множество связей;
- Сложность в использовании нескольких БД;
- Всегда только один язык программирования (стек технологий);
- Сложность версионирования, рефакторинга, разделения задач;
Микросервисы — на уровне инфраструктуры, backend разделен между разными серверами; общении зачастую по API;
Практики по работе с микросервисами:
- Микромир не связанный с другими: своя БД (SQL or NoSQL), API, свой язык;
- Простота разделения задач между разработчиками (работают над разними частями системы);
- Удобства в разработке: версионность, надежность и стабильность,легкость рефакторинга, легкое масштабирование
Минусы (из комментов @Andrey Bezsonov):
- Проблемы с коннектом между сервисами (особенно, когда их много);
- Обработка ошибок: когда один из сервисов лежит;
- Дублирование данных;
- Усложняется деплой: развёртывание локального окружения (уже без докера не развернёшь) и как итог — время разработки;
В микросервисных системах накладные расходы идут на API между сервисами. Как вычислительные (но это мелочь) так и сапортовые: (чтобы задеплоить фичу нужно будет разбить ее на несколько задачь, которые касаются разных микросервисов. У каждого свой бэклог и свой релизплан. Это как управлять оркестром.
Например: у нас есть SAAS сервис, который имеет монолитную архитектуру (все сервисы собраны вместе); в какой-то момент, при масштабировании, один модуль начинает работать медленно и тянет на дно всю систему; появляеться запрос на оптимизацию; до какого-то времени, проблему можно решить через оптимизацию.
В какой-то момент, мы снова упремся в ограничения на уровне php (например время рассчётов, скорость загрузки и тд).
Появляется необходимость перейти на микросервисы: вынесты все модули в отдельные, независимые сущности (backend), со своими БД и API для общения, подключить новые технологии и фреймворки.
Следующий шаг: снова подошли к ограничению, опять в нагрзке на один из сервисов; теперь есть смысл добавлять балансировщик нагрузки (load balancer — шина, которая позволяет отправлять запрос, на свободные сервера).
Масштабирование систем
Вертикальное — мы оцениваем, какой ресурс нам нужен, и можем плавно наращивать его объем; то есть, увеличиваем объем одного ресурса;
Горизонтальное — добавляем новые ресурсы; увеличиваем кол-во доступных ресурсов;
Например: бутылка с водой; вертикальное — увеличывать объем одной бутылки; горизонтальное — поднимать кол-во бутылок одного размера;
Шаблоны (паттерны) проектирования
Это набор практик, которые позволяют оптимально реализовывать стандартные функции программ;
Классификация:
- структурные — для организации удобного кода;
- поведенческие — реализуют поведение системы;
Принципы разработки:
Им можно следовать или нет ;)
- DRY — “don’t repeat yourself”: не нужно писать что-то повторно; можно использовать подпрограммы и переиспользовать код;
- KISS — keep it simple, stupid: не нужно придумывать хуйню на ровном месте; делать задачу/решение в лоб, без наворотов (не нужных); итерации;
- YAGNI — You aren’t gonna need it; не делай то, что тебе не нужно сразу; потом всегда можно добавить;
- SOLID — это уже целостная коцепция из 5 “шагов”;
- DDD — domen driven design; проблемно ориентированное программирование;
SOLID
Он про работу с объектами, ближе по стилю к ООП. SOLID способствует созданию такой системы, которую будет легко поддерживать и расширять в течение долгого времени.
Single responsibility — принцип единой ответсвенности: один объект — одна задача — одна ответсвенность;
Open-close — приниц открытости/закрытости; код должен быть закрыт для модификации и открыт для расширения; новые функции нужно добавлять сверху (наследование), а не расширять;
Liskov substitution — приниц подстановик Лисков; если подставить вместо родительского класса — дочерний, программа должна работать без изминений;
Interface segregation — приниц разделения интерфейсов; много унитарных, специальных интерфейсов лучше одного общего большого;
Dependency inversion — инверсия зависимостей; абстракиця рулит процессами; мы должны выносить зависимости из классов, наружу; добавлять снаружи, а не внутри;
DDD
Набор принципов и схем, направленных на создание оптимальных систем объектов.
Термины
Демон (воркер)— программа, которая запущена в фоновом режиме из консоли (не разработчиком, а другим процессом или программой), и ждет какое-то действие, для обработки;
Например: фоновая задача, которая ждет сигнала от кнопки на клавиатуре, чтобы отобразить её.