Architecture Patterns
Architecture Patterns⚑
CAP теорема⚑
CAP теорема (Consistency, Availability, Partition Tolerance) описує обмеження у розподілених системах. Вона стверджує, що неможливо одночасно забезпечити всі три властивості
- Consistency (узгодженість): Всі вузли системи бачать однакові дані в один і той самий момент часу. Будь-який запит до системи завжди повертає найновіше значення даних.
- Availability (доступність): Кожен запит отримує відповідь, незалежно від стану будь-якого окремого вузла. Система гарантує, що всі запити завершуються успішно.
- Partition Tolerance (стійкість до розділення): Система продовжує функціонувати, незважаючи на будь-які розділення мережі, які можуть розділити вузли системи на частини, що не можуть спілкуватися один з одним.
CAP теорема є основою для розуміння компромісів у проектуванні розподілених систем і допомагає приймати рішення щодо вибору архітектури залежно від вимог до системи.
Основні положення CAP теореми
- Розподілена система не може одночасно забезпечити консистентність, доступність і толерантність до розділень.
- У випадку виникнення розділення в мережі, система повинна обирати між підтримкою консистентності і доступності.
Вибір між властивостями
- CA (Consistent and Available): Система забезпечує консистентність і доступність, але може зупинитися у випадку розділення (напр., традиційні реляційні бази даних).
- CP (Consistent and Partition-tolerant): Система забезпечує консистентність і толерантність до розділень, але може бути недоступною під час розділення (напр., системи, що використовують алгоритми консенсусу).
- AP (Available and Partition-tolerant): Система забезпечує доступність і толерантність до розділень, але може повернути непослідовні дані під час розділення (напр., розподілені NoSQL бази даних).
У реальних умовах розподілених систем вибір зазвичай здійснюється між узгодженістю та доступністю, оскільки розділення мережі є неминучим явищем. Приклад: у базах даних типу NoSQL, таких як MongoDB чи Cassandra, дизайн систем часто надає перевагу доступності і стійкості до розділення, жертвуючи частковою узгодженістю.
Links
Що таке low coupling and high cohesion⚑
Low Coupling (Слабке зв'язування)
Слабке зв'язування означає, що модулі або компоненти програми мають мінімальні залежності один від одного. Це означає, що зміна в одному модулі не повинна призводити до широкомасштабних змін у інших модулях. Модулі мають бути незалежними і можуть взаємодіяти через чітко визначені інтерфейси, що спрощує розуміння та підтримку коду.
Переваги слабкого зв'язування
- Зменшення ризику впливу змін на інші частини коду.
- Зручність в розробці та розумінні окремих модулів.
- Більша можливість перевикористання коду.
High Cohesion (високе зчеплення)
Високе зчеплення означає, що в межах одного модуля або компонента повинні знаходитися лише функції та властивості, пов'язані з однією чіткою відповідальністю або функціональністю. Це означає, що кожен модуль повинен виконувати лише одну конкретну задачу, і його функції повинні бути пов'язані логічно та тісно.
Переваги високого зчеплення
- Покращення зрозумілості та підтримки коду за рахунок чіткої відповідальності модулів.
- Зниження взаємозалежності функцій у межах модуля, що полегшує розробку та тестування.
Dependency Injection - DI - Впровадження залежності⚑
Dependency Injection (DI) - шаблон проектування, який полягає в передачі залежностей об'єктам під час їх створення. Тобто ми передаємо залежність, а не створюємо її в класі. Залежність - це інший клас, який потрібен нам в нашому поточному класі (наприклад, клас для доступу в БД).
Це робить код більш гнучким, оскільки об'єкти не прив'язані до конкретних реалізацій залежностей, і їх можна легко замінити або модифікувати.
class DatabaseConnection:
def __init__(self, db_url):
self.db_url = db_url
def connect(self): # Logic to establish a database connection
pass
class UserRepository:
def __init__(self, db_connection):
self.db_connection = db_connection
def get_user(self, user_id): # Logic to retrieve user from the database using db_connection
pass
db_connection = DatabaseConnection("mysql://username:password@localhost/db_name")
user_repository = UserRepository(db_connection)
У цьому прикладі db_connection
є залежністю для UserRepository
. Dependency Injection дозволяє замінити конкретну реалізацію DatabaseConnection
, не змінюючи логіку UserRepository
.
DI контейнер⚑
Dependency Injection Container - це інструмент, який автоматизує процес введення залежностей у об'єкти програми. Він відповідає за створення та впорядкування об'єктів та їх залежностей, і надає можливість легко вносити зміни в конфігурацію додатка, замінюючи одні реалізації на інші. Тобто він містить список інтерфейсів та їх реалізацій.
Hexagonal vs onion архітектура⚑
Hexagonal та onion архітектури є популярними підходами до побудови програмних систем, які спрямовані на підвищення гнучкості, зручності тестування та ізоляції бізнес-логіки від зовнішніх залежностей. Обидва підходи мають спільну мету, але різні акценти. Hexagonal архітектура більше зосереджується на вхідних і вихідних портах, тоді як Onion архітектура організована у вигляді шарів навколо ядра. Hexagonal архітектура використовує порти та адаптери для взаємодії з зовнішніми компонентами. Onion архітектура будується на кількох шарах, кожен з яких має свою відповідальність.
Hexagonal (порт-адаптерна архітектура) Головна мета — відокремити ядро програми від зовнішніх компонентів, таких як база даних, користувацький інтерфейс або інші сервіси. Орієнтується на інтеграцію з зовнішнім світом через порти (інтерфейси) і адаптери. Ядро додатку спілкується з зовнішніми компонентами через порти, які є інтерфейсами. Адаптери реалізують ці інтерфейси і забезпечують зв'язок з реальними компонентами. Основна ідея: програма працює через чітко визначені точки входу та виходу. Це дозволяє легко замінювати зовнішні компоненти, такі як бази даних або веб-інтерфейси, без змін у бізнес-логіці. Використовує метафору шестикутника для ілюстрації, де кожна сторона представляє різні адаптери (наприклад, REST API, CLI, база даних).
Onion (цибулева архітектура) Цибулева архітектура також спрямована на відокремлення ядра програми від інфраструктурних компонентів. Вона організована у вигляді шарів, які оточують ядро. Фокусується на ізоляції доменної логіки від залежностей через шарову організацію.
У центрі знаходиться доменна модель, яка не знає нічого про зовнішній світ. Інші шари оточують її і взаємодіють один з одним через інтерфейси. Шари відповідають за доступ до бази даних, API або користувацького інтерфейсу. Центральний шар (ядро) містить найважливішу бізнес-логіку. Цибулева структура акцентує увагу на суворій спрямованості залежностей — залежності рухаються лише з зовнішніх шарів до внутрішніх.
Моноліт⚑
Моноліт — це архітектурний підхід до розробки програмного забезпечення, в якому вся функціональність додатку зосереджена в одній єдиній кодовій базі та розгортається як єдине ціле. Це традиційний і часто простіший підхід до побудови систем, але з часом може створювати певні виклики у підтримці та масштабуванні.
Усі компоненти програми (бізнес-логіка, інтерфейс користувача, доступ до бази даних) знаходяться в одному додатку. Логіка взаємодії між компонентами часто реалізується через прямі виклики методів або функцій.
Моноліт підходить для невеликих команд і проєктів із передбачуваними обсягами роботи. Його також використовують для MVP (мінімально життєздатного продукту), де швидкість розробки важливіша за складні архітектурні рішення.
Моноліт часто є першим кроком у розробці, але з часом додатки можуть бути розділені на мікросервіси для підвищення гнучкості та масштабованості.
Переваги моноліту
- Простота розробки та деплойменту, особливо на ранніх етапах проєкту.
- Можливість розгорнути весь додаток як один артефакт, що спрощує адміністрування.
- Менші накладні витрати на мережеву взаємодію між компонентами.
Недоліки моноліту
- З часом ускладнюється підтримка великого коду, особливо якщо команда зростає.
- Масштабування потребує збільшення ресурсів для всього додатку, навіть якщо навантаження створює лише одна його частина.
- Тісно пов’язані компоненти можуть ускладнювати впровадження нових технологій або змін.
Links
Microservices - Мікросервіси⚑
Мікросервіси — це архітектурний стиль, який передбачає розбиття програми на набір незалежних компонентів (сервісів), кожен з яких відповідає за окрему бізнес-функцію і може розгортатися та масштабуватися окремо. Цей підхід спрямований на підвищення гнучкості, простоти підтримки та масштабування великих систем.
Кожен сервіс є незалежним, має свою кодову базу, базу даних (або доступ до окремих таблиць) і виконує одне конкретне завдання. Взаємодія між сервісами зазвичай відбувається через легкі протоколи, такі як HTTP (REST, GraphQL) або брокери повідомлень (RabbitMQ, Kafka).
Підходить для великих і динамічних проєктів, де важлива можливість масштабувати окремі частини системи або швидко впроваджувати зміни. Мікросервіси дозволяють створювати гнучкі та масштабовані системи, але потребують ретельного планування, щоб уникнути зростання складності в управлінні.
Переваги мікросервісів
- Зручність масштабування: кожен сервіс можна масштабувати незалежно від інших.
- Можливість використовувати різні технології для різних сервісів залежно від їхніх потреб.
- Полегшення підтримки: зміни в одному сервісі не впливають на інші (за умови чітких контрактів).
- Зменшення ризику: збій в одному сервісі не зупиняє роботу всієї системи.
Недоліки мікросервісів
- Ускладнена інфраструктура: потрібен надійний механізм для оркестрації сервісів, моніторингу та логування.
- Накладні витрати на комунікацію: виклики між сервісами додають латентність і вимагають захисту від помилок.
- Складнощі в забезпеченні узгодженості даних між сервісами.
Моноліт vs Мікросервіси⚑
Summary
Моноліт підходить для простих рішень і швидкого старту, тоді як мікросервіси необхідні для великих, складних проєктів, які вимагають масштабованості та модульності.
Вибір між монолітною архітектурою та мікросервісами залежить від масштабів проєкту, команди та бізнес-вимог. Кожен підхід має свої сильні сторони, які слід враховувати залежно від конкретної ситуації.
Коли обрати Моноліт
- Проєкт невеликий, із чітко визначеною функціональністю та обмеженими масштабами.
- Команда розробників мала, і всі члени працюють над одним кодом.
- Потрібно швидко створити MVP (мінімально життєздатний продукт).
- Інфраструктурні ресурси обмежені, немає необхідності в складній оркестрації.
- Немає значних вимог до незалежного масштабування різних компонентів.
- Основний фокус на простоті розробки, підтримки та розгортання.
Коли обрати Мікросервіси
- Проєкт великий і передбачає постійне зростання як за функціональністю, так і за навантаженням.
- Є чітка потреба в масштабуванні окремих частин системи, наприклад, модуля оплати чи пошуку.
- У проєкті працює велика команда, яка може бути розділена на кілька груп, кожна з яких відповідає за окремий сервіс.
- Проєкт потребує високої гнучкості у виборі технологій для різних частин системи.
- Необхідно забезпечити високу надійність: збої в одному сервісі не повинні впливати на роботу інших.
- Бізнес-вимоги швидко змінюються, і потрібна модульність для швидкого впровадження нових функцій.