Модуль 40

Архітектура production-рівня та фінальний проєкт

Ось готовий урок, створений за твоїм майстер-промптом.


🎓 CS50: Архітектура production-рівня та фінальний проєкт

Привіт, друзі! Це CS50, і сьогодні ми переходимо від написання коду до створення програмного забезпечення.


1. 🔥 Вступ: Від "працює у мене" до "працює для мільйонів"

Уявіть ситуацію. Ви написали крутий скрипт. Він парсить дані, рахує статистику і видає результат. Ви запускаєте його на своєму ноутбуці — все літає! 🚀 Ви показуєте другу — він у захваті.

Але тут ви вирішуєте викласти це в інтернет. І раптом...

  • Заходить 100 користувачів одночасно — сервер "падає".
  • Ви хочете змінити колір кнопки, а ламається база даних (звучить абсурдно, але в "спагеті-коді" буває й таке).
  • Вам треба додати колегу в проєкт, а він три дні не може запустити код, бо у нього "не ті бібліотеки".

Запитання до вас: Чим відрізняється будівництво собачої будки на задньому дворі від будівництва хмарочоса в центрі Нью-Йорка?

І там, і там є стіни й дах. Але якщо ви будуєте хмарочос так само як будку — просто збиваючи дошки цвяхами, як заманеться — він завалиться від першого ж вітру.

Архітектура production-рівня — це про те, як будувати хмарочоси. Це набір правил, які гарантують, що ваш застосунок буде: 1. Надійним (не впаде). 2. Масштабованим (витримає ріст). 3. Зрозумілим (інші програмісти не захочуть вас проклинати).

Сьогодні ми розберемося, як перетворити ваш фінальний проєкт із "лабораторної роботи" на професійний продукт.


2. 🧠 Теоретична база: Принцип "Ресторану"

Головне правило архітектури — Separation of Concerns (Розділення відповідальності).

Давайте уявимо професійний ресторан. Тут є три ключові ролі:

  1. Офіціант (Interface / Controller): Приймає замовлення від клієнта, приносить меню, несе їжу. Він не готує їжу і не бігає на ферму за картоплею.
  2. Шеф-кухар (Business Logic / Service): Отримує замовлення від офіціанта, знає рецепти, обробляє продукти. Він не спілкується з клієнтом напряму.
  3. Комірник (Database / Repository): Знає, де лежить картопля, видає її кухарю. Він не знає, що з неї приготують — пюре чи фрі. Його задача — надійне зберігання.

Що відбувається "під капотом"?

У поганому коді (спагеті) ви пишете все в одному файлі: тут же запит до бази, тут же перевірка пароля, тут же малювання HTML. Це якби офіціант сам біг на склад, хапав сире м'ясо і смажив його прямо на столі у клієнта.

У Production-архітектурі ми ділимо код на шари:

  • Presentation Layer (Шар представлення): Те, що бачить юзер (або API endpoints).
  • Business Logic Layer (Шар бізнес-логіки): "Мозок" програми. Тут живуть правила (наприклад, "користувач не може переказати гроші, якщо баланс < 0").
  • Data Access Layer (Шар даних): Лише читання/запис у Базу Даних.

❗️ Запам’ятайте: Один компонент — одна відповідальність. Якщо функція робить більше однієї речі (і читає файл, і рахує суму) — це погана архітектура.


3. 🧪 Приклади: Еволюція коду

Давайте подивимось, як змінюється код від "новачка" до "профі".

Рівень 1: "Все в купу" (Bad Practice ❌)

Уявіть, що ми робимо реєстрацію користувача.

# main.py
import sqlite3

def register_user(username, password):
    # 1. Перевірка даних (Логіка)
    if len(password) < 8:
        return "Пароль надто короткий!"

    # 2. Робота з БД (Дані)
    conn = sqlite3.connect('users.db')
    cursor = conn.cursor()
    cursor.execute(f"INSERT INTO users VALUES ('{username}', '{password}')") # До речі, тут SQL Injection!
    conn.commit()

    # 3. Відповідь (Представлення)
    return "<h1>Успішно!</h1>"

Що тут не так? Якщо ми захочемо змінити базу даних на PostgreSQL, нам доведеться лізти в логіку реєстрації. Якщо захочемо змінити HTML на JSON — теж.


Рівень 2: Шаруватя архітектура (Good Practice ✅)

Питання до вас: Куди ми маємо винести перевірку довжини пароля, а куди — SQL запит?

Давайте розділимо:

1. Repository (Комірник):

# repository.py
def save_user_to_db(user_data):
    # Тільки зберігає. Ніяких перевірок бізнес-логіки.
    db.execute("INSERT INTO users...", user_data)

2. Service (Шеф-кухар):

# service.py
import repository

def create_user(username, password):
    # Чиста бізнес-логіка
    if len(password) < 8:
        raise ValueError("Пароль слабкий")

    # Хешування пароля (безпека!)
    hashed_pw = hash_password(password)

    # Делегуємо збереження
    repository.save_user_to_db({'name': username, 'pwd': hashed_pw})

3. Controller (Офіціант):

# controller.py
import service

def handle_registration_request(request):
    try:
        data = request.json
        service.create_user(data['username'], data['password'])
        return {"status": "success", "code": 201} # JSON відповідь
    except ValueError as e:
        return {"error": str(e), "code": 400}

Чому це краще? Якщо завтра ми змінимо базу даних, ми змінимо тільки файл repository.py. Service і Controller навіть не дізнаються про це. Система стала гнучкою!


4. 🛠 Практична частина

Час закачати рукави. Ось ваші завдання для підготовки до фінального проєкту.

Завдання 1: Детектив коду 🕵️‍♂️ Подивіться на цей шматок псевдокоду. До якого шару (Controller, Service, Repository) він належить?

Код перевіряє, чи є у користувача преміум-підписка, перш ніж дозволити завантажити 4K відео.

Завдання 2: Рефакторинг 🧹 У вас є функція send_email_to_client(email, message). Всередині неї жорстко прописано: smtpserver = "smtp.gmail.com" і пароль "mysecretpassword". * Чому це небезпечно для production? * Перепишіть логіку так, щоб конфігурація бралася із змінних середовища (Environment Variables).

Завдання 3: "A що, якщо..." 🤔 Ви робите інтернет-магазин. * Ситуація: Сервіс оплати (наприклад, LiqPay) тимчасово не відповідає. * Задача: Як має поводитися ваша архітектура? Впасти з помилкою 500? Чи показати користувачеві: "Спробуйте пізніше", зберігши замовлення в статусі "Очікує оплати"? Опишіть алгоритм.

Завдання 4: Міні-кейс для Фінального Проєкту 🚀 Придумайте ідею свого фінального проєкту. Тепер розбийте її на три папки: 1. Які дані ви будете зберігати? (Таблиці БД) 2. Які ключові функції виконуватиме програма? (Бізнес-логіка) 3. Як користувач взаємодіятиме з нею? (Web, Telegram Bot, Mobile App)


5. 💡 Мислення як у розробника

Як думає досвідчений інженер, коли починає проєкт?

  1. Не вигадуй велосипед (DRY - Don't Repeat Yourself). Якщо є готова, перевірена бібліотека для авторизації — використовуй її. Не пиши свою криптографію, благаю!
  2. Конфіги — окремо. Ніколи, чуєте, НІКОЛИ не комітьте паролі та API-ключі в GitHub. Використовуйте .env файли. Це відрізняє профі від аматора.
  3. Думай про майбутнього себе. Через 3 місяці ви відкриєте цей код. Якщо там змінні називаються a, b, x — ви захочете плакати. Пишіть код так, ніби його буде підтримувати маніяк, який знає, де ви живете.

Типова помилка новачка: Намагатися зробити "ідеально" одразу. Порада: Зробіть MVP (Minimal Viable Product) — мінімально працюючу версію. Нехай вона буде некрасива, але вона повинна працювати від початку до кінця. Покращувати будете потім.


6. 🧩 Підсумок

Отже, що ми сьогодні вивчили?

  • Ми зрозуміли, що архітектура — це скелет вашого проєкту. Без неї організм розвалиться.
  • Ми навчилися розділяти відповідальність: офіціант не готує, кухар не носить тарілки.
  • Ми побачили, як структурувати фінальний проєкт, щоб він був Modular (модульним) і Maintainable (підтримуваним).

Тепер ви не просто пишете рядки коду. Ви проєктуєте системи. Ви готові до фінального проєкту.

Наступного разу: Ми поговоримо про те, як взяти ваш код і запустити його "у хмарі", щоб увесь світ міг його побачити. Готуйтеся до Deployment!

А це був CS50. (Драматична пауза, усмішка) Успіхів з кодом!