Профессия Fullstack-разработчик: от архитектуры до деплоя

Комплексный курс, охватывающий все этапы создания веб-приложений: от верстки интерфейсов до настройки серверов. Вы изучите взаимодействие клиентской и серверной частей, работу с базами данных и современные инструменты разработки.

1. Основы веб-разработки: HTML, CSS и глубокое погружение в JavaScript

Основы веб-разработки: HTML, CSS и глубокое погружение в JavaScript

Добро пожаловать на курс «Профессия Fullstack-разработчик». Мы начинаем наше путешествие с фундамента, на котором держится весь современный веб. Даже если вы планируете заниматься преимущественно серверной частью (Backend), понимание того, как работает клиентская сторона (Frontend), критически важно для архитектора системы.

Веб-страница — это не просто картинка, это сложный механизм, состоящий из трех основных слоев:

  • Структура (HTML)
  • Представление (CSS)
  • Поведение (JavaScript)
  • В этой статье мы разберем эти технологии не на уровне «как вставить картинку», а на уровне инженерного понимания их взаимодействия.

    HTML: Больше, чем просто теги

    HTML (HyperText Markup Language) — это скелет вашего приложения. Многие новички совершают ошибку, рассматривая HTML только как способ размещения элементов на экране. Профессиональный Fullstack-разработчик должен мыслить категориями семантики и DOM (Document Object Model).

    Семантическая верстка

    Семантика в HTML означает использование тегов, которые передают смысл содержимого, а не его внешний вид. Браузеры, поисковые системы и скринридеры (программы для чтения с экрана) опираются именно на семантику.

    Вместо бесконечных div, используйте:

    * header — для вводной части страницы или раздела. * nav — для навигационных ссылок. * main — для основного содержимого. * article — для независимого контента (например, пост в блоге). * section — для тематической группировки контента. * aside — для контента, косвенно связанного с основным (боковая панель).

    DOM-дерево

    Когда браузер получает HTML-код, он преобразует его в древовидную структуру объектов — DOM. Это живая структура, с которой мы будем взаимодействовать через JavaScript.

    !Визуализация структуры Document Object Model (DOM), показывающая иерархию элементов на веб-странице

    CSS: Геометрия и каскад

    CSS (Cascading Style Sheets) отвечает за то, как элементы выглядят. Но для разработчика важнее понимать, как они занимают место в пространстве. Здесь в игру вступает блочная модель (Box Model).

    Блочная модель

    Каждый элемент в HTML — это прямоугольный бокс. Понимание того, как рассчитывается его размер, избавляет от 90% проблем с версткой.

    Размер элемента складывается из нескольких компонентов. Рассмотрим формулу расчета полной ширины элемента:

    Где: * — полная ширина, которую элемент занимает на странице. * — ширина контента (свойство width). * — внутренние отступы (padding) слева и справа. * — толщина границ (border) слева и справа. * — внешние отступы (margin) слева и справа.

    !Схематичное изображение блочной модели CSS, демонстрирующее слои Content, Padding, Border и Margin

    > Важно: Свойство box-sizing: border-box изменяет это поведение, включая padding и border в общую ширину width, что значительно упрощает расчеты.

    JavaScript: Глубокое погружение

    JavaScript — это язык программирования, который «оживляет» страницу. Для Fullstack-разработчика JS является основным инструментом, так как (благодаря Node.js) он используется и на сервере.

    Переменные и области видимости

    В современном стандарте ES6+ мы отказались от var в пользу let и const.

    const: Используется по умолчанию. Создает константу, которую нельзя переназначить. Однако, если это объект, его свойства можно* менять. * let: Используется, когда значение переменной будет меняться.

    Ключевое отличие var от let/const — это область видимости (Scope). var имеет функциональную область видимости, а let и const — блочную (ограниченную фигурными скобками {}).

    Типы данных

    В JavaScript существует 8 типов данных. Их можно разделить на две категории: примитивы и объекты.

    Примитивы (передаются по значению):

  • String
  • Number
  • Boolean
  • Null
  • Undefined
  • Symbol
  • BigInt
  • Объекты (передаются по ссылке):

  • Object (включая Array, Function, Date и т.д.)
  • Понимание передачи по ссылке критически важно. Если вы скопируете объект const a = { x: 1 }; const b = a;, то изменение b.x изменит и a.x, так как обе переменные ссылаются на одну и ту же область памяти.

    Асинхронность и Event Loop

    JavaScript — однопоточный язык. Это означает, что он может выполнять только одну операцию в единицу времени. Но как тогда браузер обрабатывает клики, загружает данные с сервера и показывает анимацию одновременно? Ответ кроется в Event Loop (Цикл событий).

    Механизм работы выглядит так:

  • Call Stack (Стек вызовов): Здесь выполняется синхронный код. LIFO (Last In, First Out).
  • Web APIs: Асинхронные операции (таймеры, AJAX-запросы) передаются сюда и выполняются браузером параллельно.
  • Callback Queue (Очередь задач): Когда асинхронная операция завершена, ее колбэк попадает в очередь.
  • Event Loop: Бесконечный цикл, который проверяет: «Пуст ли стек вызовов?». Если пуст, он берет первую задачу из очереди и помещает её в стек.
  • !Визуализация цикла событий (Event Loop), объясняющая асинхронную природу JavaScript

    Для работы с асинхронностью мы используем Promises и синтаксис async/await.

    Пример асинхронной функции:

    Манипуляция DOM

    JavaScript взаимодействует с HTML через DOM API. Это набор методов, позволяющих искать элементы, изменять их и реагировать на события.

    Основные методы: * document.querySelector(selector) — возвращает первый элемент, соответствующий CSS-селектору. * element.addEventListener(event, handler) — добавляет обработчик события (например, клика). * element.innerHTML / element.textContent — чтение и изменение содержимого.

    Взаимодействие технологий

    Когда вы открываете сайт, происходит следующий процесс:

  • Браузер загружает HTML и строит DOM.
  • Браузер загружает CSS и строит CSSOM (CSS Object Model).
  • DOM и CSSOM объединяются в Render Tree (Дерево рендеринга).
  • Происходит Layout (Компоновка) — расчет геометрии каждого элемента (где он находится и какого он размера).
  • Происходит Paint (Отрисовка) — заполнение пикселей на экране.
  • JavaScript может вмешаться в этот процесс на любом этапе, изменяя DOM или стили, что вызывает повторный Layout и Paint (или Reflow и Repaint).

    Заключение

    Мы рассмотрели «святую троицу» веб-разработки. HTML создает смысл и структуру, CSS отвечает за визуальное восприятие и геометрию, а JavaScript управляет логикой и данными. В следующих статьях мы перейдем к архитектуре приложений и начнем строить более сложные системы, но понимание этих основ останется с вами навсегда.

    В следующем модуле мы углубимся в работу протоколов HTTP и то, как клиент общается с сервером.

    2. Frontend-разработка: современные фреймворки, SPA и управление состоянием приложения

    Frontend-разработка: современные фреймворки, SPA и управление состоянием приложения

    В предыдущей статье мы разобрали, как браузер строит DOM-дерево и как JavaScript позволяет нам манипулировать им напрямую. Мы научились находить элементы через querySelector и менять их стили или содержимое. Это отлично работает для простых страниц, но что происходит, когда приложение становится сложным?

    Представьте социальную сеть. У вас есть лента новостей, чаты, уведомления, профиль пользователя. Если пытаться управлять всем этим через прямые манипуляции DOM (Imperative approach), ваш код очень быстро превратится в «спагетти». Вы будете терять связи между данными и их отображением.

    Именно здесь на сцену выходят современные фреймворки (React, Vue, Angular) и архитектурный подход SPA (Single Page Application).

    Эволюция архитектуры: от MPA к SPA

    Традиционный веб работал по модели MPA (Multi-Page Application). Каждый клик по ссылке отправлял запрос на сервер, сервер собирал новую HTML-страницу и отправлял её обратно. Браузер полностью перезагружал страницу.

    SPA (Single Page Application) меняет правила игры. При первой загрузке вы получаете один HTML-файл (обычно почти пустой) и большой JavaScript-файл (bundle). Далее:

  • JavaScript перехватывает навигацию.
  • При переходе по ссылке страница не перезагружается.
  • JavaScript запрашивает у сервера только «сырые» данные (обычно в формате JSON).
  • JavaScript сам генерирует HTML на основе этих данных и вставляет его в страницу.
  • !Сравнение жизненного цикла Multi-Page Application и Single Page Application

    Это делает веб-приложения похожими на нативные программы: они быстрые, отзывчивые и плавные.

    Компонентный подход и Декларативное программирование

    Современные фреймворки исповедуют компонентный подход. Интерфейс разбивается на независимые, переиспользуемые блоки — компоненты. Кнопка, поле ввода, карточка товара, шапка сайта — всё это компоненты.

    Но главное изменение — это переход от императивного стиля к декларативному.

    * Императивный (как в jQuery/Vanilla JS): «Найди элемент с ID 'btn', добавь ему класс 'active', измени текст на 'Loading'». * Декларативный (React/Vue): «Кнопка должна выглядеть так, если состояние загрузки истинно».

    Мы описываем что хотим получить, а фреймворк сам решает как это сделать.

    Формула UI

    В мире современной разработки интерфейс можно описать простой математической концепцией:

    Где: * (User Interface) — то, что пользователь видит на экране. * (Function) — ваш компонент или фреймворк, который преобразует данные в визуальное представление. * (State) — состояние приложения (данные).

    Это означает, что интерфейс является функцией от состояния. Если вы хотите изменить что-то на экране, вы не трогаете DOM. Вы меняете (данные), и обновляется автоматически.

    Virtual DOM: Секрет производительности

    Если при каждом изменении данных перерисовывать всю страницу, приложение будет работать очень медленно. Операции с реальным DOM — самые дорогие в браузере.

    Чтобы решить эту проблему, была придумана концепция Virtual DOM.

    Virtual DOM — это легковесная копия реального DOM, хранящаяся в памяти в виде обычных JavaScript-объектов. Когда состояние приложения меняется, происходит следующий процесс:

  • Render: Создается новое дерево Virtual DOM на основе новых данных.
  • Diffing (Сравнение): Фреймворк сравнивает новое дерево со старым (предыдущим состоянием).
  • Reconciliation (Согласование): Вычисляется минимальный набор изменений, необходимых для обновления реального DOM.
  • Patch: Эти точечные изменения применяются к реальному браузеру.
  • !Процесс обновления интерфейса через Virtual DOM

    Благодаря этому мы можем писать код так, будто перерисовываем всё приложение целиком, но фреймворк оптимизирует это до точечных операций.

    Управление состоянием (State Management)

    Понимание того, как управлять данными (State) — ключевой навык Fullstack-разработчика. В простых приложениях данные живут внутри компонентов. Но что делать, если данные нужны в разных частях приложения?

    Проблема Prop Drilling

    Представьте дерево компонентов: App -> Header -> UserMenu -> Avatar

    Если данные о пользователе (аватарка) хранятся в App, их нужно передать через Header и UserMenu, даже если этим промежуточным компонентам эти данные не нужны. Это называется Prop Drilling (сверление пропсов).

    Глобальное состояние (Global Store)

    Для решения этой проблемы используются менеджеры состояний (Redux, Vuex, Pinia, MobX, Context API). Идея заключается в создании единого источника правды (Store), который находится вне дерева компонентов.

    Любой компонент может:

  • Подписаться на данные из Store.
  • Отправить сигнал (Action) об изменении данных.
  • Однонаправленный поток данных (Unidirectional Data Flow)

    Это паттерн, который делает поведение приложения предсказуемым. Данные всегда текут в одну сторону.

    Где: * — событие (например, клик кнопки «Купить»). * — механизм, передающий событие в хранилище. * — место, где живут данные и логика их изменения. * — отображение, которое обновляется при изменении Store.

    !Цикл однонаправленного потока данных

    Реактивность

    Реактивность — это магия, благодаря которой переменная count = 0 превращается в count = 1, и число на экране меняется само.

    Существует два основных подхода:

  • Pull-based (React): Мы явно говорим системе «перерисуй» (например, вызывая setState). Система запускает процесс сравнения Virtual DOM.
  • Push-based (Vue, Svelte, MobX): Система использует «обертки» (Proxy или геттеры/сеттеры) вокруг данных. Когда вы меняете переменную state.count++, она сама «уведомляет» всех, кто от неё зависит, что нужно обновиться.
  • Клиентский роутинг

    В SPA за URL в адресной строке отвечает не сервер, а JavaScript. Библиотеки роутинга (например, React Router) следят за History API браузера.

    Когда вы кликаете по ссылке /profile:

  • Роутер блокирует стандартный переход браузера.
  • Меняет URL в строке адреса через history.pushState().
  • Смотрит в свою конфигурацию: «Какой компонент я должен показать для пути /profile?».
  • Подменяет содержимое страницы на компонент профиля.
  • Всё это происходит мгновенно, без обращения к серверу за HTML-файлом.

    Заключение

    Современный Frontend — это сложная инженерная дисциплина. Мы больше не просто верстаем страницы, мы строим полноценные приложения в браузере. Мы используем:

    * SPA для плавности работы. * Компоненты для повторного использования кода. * Virtual DOM для производительности. * Global Store для управления сложными данными.

    Как Fullstack-разработчик, вы должны понимать, что Frontend теперь берет на себя часть логики, которая раньше была на сервере (валидация, роутинг, частичное кеширование данных). В следующем модуле мы перейдем на «тёмную сторону» — к Backend-разработке, и узнаем, как создать API, которое будет питать данными наше SPA-приложение.

    3. Backend-разработка: построение API, серверная логика и аутентификация пользователей

    Backend-разработка: построение API, серверная логика и аутентификация пользователей

    В предыдущем модуле мы научились создавать интерактивные пользовательские интерфейсы (SPA), используя современные фреймворки. Мы знаем, как отрисовать кнопку, форму входа и список товаров. Но если нажать на кнопку «Купить», ничего не произойдет, кроме изменения состояния в памяти браузера. Как только пользователь обновит страницу, все данные исчезнут.

    Чтобы приложение стало полноценным продуктом, ему нужна память и логика, которая работает независимо от браузера пользователя. Добро пожаловать в мир Backend-разработки.

    Что такое Backend?

    Backend — это «подкапотная» часть веб-приложения. Это код, который выполняется на сервере, а не в браузере клиента. Если Frontend отвечает за то, как выглядит приложение, то Backend отвечает за то, как оно работает с данными.

    Основные задачи Backend-разработчика:

  • API (Application Programming Interface) — создание интерфейса для обмена данными с фронтендом.
  • Работа с базой данных — сохранение, чтение и изменение информации.
  • Бизнес-логика — правила, по которым работает приложение (например, расчет скидки или проверка доступности товара).
  • Безопасность — аутентификация и авторизация пользователей.
  • Так как наш курс посвящен Fullstack-разработке на JavaScript, в качестве серверной среды мы будем использовать Node.js. Это среда выполнения, которая позволяет запускать JavaScript вне браузера.

    Архитектура клиент-серверного взаимодействия

    В основе веба лежит протокол HTTP (HyperText Transfer Protocol). Взаимодействие всегда инициирует клиент (браузер).

    !Визуализация цикла запрос-ответ между клиентом, сервером и базой данных

    Структура HTTP-запроса

    Каждый раз, когда фронтенд запрашивает данные, он отправляет пакет информации, содержащий:

    * Метод (Method): Действие, которое нужно совершить. * URL (Path): Адрес ресурса. * Заголовки (Headers): Служебная информация (например, тип данных или токен авторизации). * Тело (Body): Данные, передаваемые на сервер (актуально для POST/PUT).

    REST API

    Самый популярный стандарт проектирования API — это REST (Representational State Transfer). Он диктует правила использования HTTP-методов:

    * GET — получение данных (например, список пользователей). Безопасный метод, не меняет состояние сервера. * POST — создание нового ресурса (например, регистрация пользователя). * PUT / PATCH — обновление существующего ресурса. * DELETE — удаление ресурса.

    Сервер, обработав запрос, возвращает Status Code:

    * 2xx (Success): Всё прошло успешно (200 OK, 201 Created). * 3xx (Redirection): Ресурс перемещен. * 4xx (Client Error): Ошибка на стороне клиента (400 Bad Request, 401 Unauthorized, 404 Not Found). * 5xx (Server Error): Ошибка на стороне сервера (500 Internal Server Error).

    Серверная логика и слои приложения

    Новички часто пишут весь код в одном файле. Это плохая практика. Профессиональная архитектура подразумевает разделение ответственности. Обычно выделяют три слоя:

  • Controller (Контроллер): Принимает HTTP-запрос, валидирует входные данные и отправляет ответ. Он не должен содержать сложной логики.
  • Service (Сервис): Содержит бизнес-логику. Здесь происходят вычисления, проверки и принятие решений.
  • Repository / Data Access Layer (DAL): Отвечает за прямое общение с базой данных.
  • Такое разделение позволяет легко тестировать код и менять базу данных без переписывания всего приложения.

    Аутентификация и безопасность

    Одной из самых критичных задач бэкенда является управление доступом. Здесь важно различать два понятия:

    * Аутентификация (Authentication): Ответ на вопрос «Кто ты?». (Проверка логина и пароля). * Авторизация (Authorization): Ответ на вопрос «Что тебе можно делать?». (Проверка прав доступа).

    Хеширование паролей

    Никогда не храните пароли в открытом виде! Если злоумышленник получит доступ к базе данных, он украдет пароли всех пользователей.

    Вместо пароля мы храним его хеш. Хеш-функция — это математический алгоритм, который преобразует строку любой длины в строку фиксированной длины. Это односторонний процесс.

    Математически это можно записать так:

    Где: * — итоговый хеш, который сохраняется в базе данных. * — криптографическая хеш-функция (например, bcrypt или Argon2). * — исходный пароль пользователя. * — соль (случайная строка, добавляемая к паролю для защиты от атак по радужным таблицам).

    Когда пользователь пытается войти, мы хешируем введенный им пароль с той же солью и сравниваем результат с тем, что лежит в базе.

    JWT (JSON Web Token)

    В современных SPA-приложениях для поддержки сессии пользователя используется стандарт JWT. Это токен, который сервер выдает клиенту после успешного входа.

    JWT состоит из трех частей, разделенных точкой:

  • Header: Информация об алгоритме шифрования.
  • Payload: Полезные данные (ID пользователя, роль, срок действия).
  • Signature: Цифровая подпись, гарантирующая, что токен не был изменен.
  • Формирование подписи — ключевой момент безопасности. Оно происходит по следующей формуле:

    Где: * — итоговая цифровая подпись (Signature). * — алгоритм хеширования с использованием ключа (Keyed-Hash Message Authentication Code). * — закодированный заголовок токена. * — закодированная полезная нагрузка (Payload). * — секретный ключ, который хранится только на сервере и никогда не передается клиенту.

    !Структура и принцип генерации подписи JSON Web Token

    Клиент сохраняет этот токен (обычно в localStorage или cookie) и прикрепляет его к каждому запросу в заголовке Authorization. Сервер проверяет подпись, используя свой секретный ключ. Если подпись совпадает — запрос одобряется.

    Middleware (Промежуточное ПО)

    В Node.js фреймворках (например, Express) широко используется концепция Middleware. Это функции, которые выполняются последовательно в цепочке обработки запроса.

    Представьте конвейер на заводе:

  • Запрос пришел.
  • Middleware 1: Логирует время поступления.
  • Middleware 2: Проверяет наличие JWT токена (Аутентификация).
  • Middleware 3: Парсит JSON из тела запроса.
  • Контроллер: Обрабатывает логику и отправляет ответ.
  • Это позволяет выносить общую логику (проверку прав, логирование, обработку ошибок) из бизнес-логики контроллеров.

    Заключение

    Backend — это фундамент надежности вашего приложения. Правильно спроектированный API, безопасная аутентификация и чистая архитектура позволяют приложению масштабироваться и работать стабильно.

    В этой статье мы разобрали основы HTTP, REST, слоистую архитектуру и безопасность через JWT. В следующем модуле мы углубимся в работу с данными: изучим базы данных, SQL и ORM, чтобы наше приложение обрело долгосрочную память.

    4. Работа с данными: проектирование реляционных и NoSQL баз данных

    Работа с данными: проектирование реляционных и NoSQL баз данных

    В предыдущем модуле мы создали серверную часть приложения (Backend), научились обрабатывать HTTP-запросы и даже реализовали аутентификацию через JWT. Однако, пока что наши данные «висят в воздухе». Если перезагрузить сервер, все зарегистрированные пользователи и созданные товары исчезнут. Чтобы приложение стало полезным, ему нужна долгосрочная память.

    В этой статье мы разберем, как проектировать хранение данных, чем отличаются реляционные базы от NoSQL, и как выбрать правильный инструмент для вашего Fullstack-проекта.

    Что такое База Данных и СУБД?

    Часто новички путают понятия «База данных» и «СУБД».

    * База данных (БД) — это само упорядоченное хранилище данных (файлы на диске). * СУБД (Система Управления Базами Данных) — это программное обеспечение, которое позволяет создавать базы данных, читать из них информацию и обновлять её (например, PostgreSQL, MySQL, MongoDB).

    Для разработчика СУБД — это «черный ящик», которому мы отправляем команды (запросы), а он возвращает результаты, заботясь о том, как именно байты записаны на жесткий диск.

    Реляционные базы данных (SQL)

    Реляционные базы данных (RDBMS) доминируют в индустрии уже более 40 лет. Самые популярные представители: PostgreSQL, MySQL, Oracle, Microsoft SQL Server.

    Слово «реляционный» происходит от английского relation (отношение). Данные здесь хранятся в таблицах, которые строго структурированы и связаны между собой.

    Структура: Таблицы, Строки и Столбцы

    Представьте таблицу Excel, но с очень строгими правилами:

  • У каждой таблицы есть имя (например, users).
  • Столбцы (колонки) определяются заранее. Вы не можете записать текст в колонку, предназначенную для чисел.
  • Каждая строка — это отдельная запись (сущность).
  • !Визуализация связи между таблицами пользователей и заказов через внешний ключ

    Первичные и Внешние ключи

    Чтобы система работала целостно, используются два фундаментальных понятия:

    * Primary Key (Первичный ключ): Уникальный идентификатор строки в таблице (обычно id). Двух строк с одинаковым id быть не может. * Foreign Key (Внешний ключ): Поле, которое ссылается на Primary Key другой таблицы. Именно так создаются связи.

    Язык SQL

    Для общения с реляционными базами используется язык SQL (Structured Query Language). Это декларативный язык: мы говорим что хотим получить, а СУБД сама решает, как это найти.

    Пример запроса:

    Индексы и производительность

    Когда данных становится много (миллионы строк), простой перебор (Full Scan) работает слишком медленно. Для ускорения поиска используются индексы. Индекс — это специальная структура данных (обычно B-Tree), которая хранит отсортированные значения и ссылки на строки.

    Эффективность поиска по индексу можно описать логарифмической сложностью:

    Где: * — время, необходимое для поиска записи. * — «О» большое, описывающее верхнюю границу сложности алгоритма. * — логарифм по основанию 2. * — общее количество записей в таблице.

    Это означает, что если количество записей увеличится в 1000 раз, время поиска вырастет лишь незначительно (на константное количество операций), в отличие от линейного поиска, где время выросло бы в 1000 раз.

    ACID: Гарантия надежности

    Главное преимущество реляционных баз — это транзакционность, соответствующая стандарту ACID:

  • Atomicity (Атомарность): Транзакция выполняется целиком или не выполняется вовсе. Если в процессе перевода денег отключилось электричество, деньги не спишутся со счета отправителя, если они не зачислились получателю.
  • Consistency (Согласованность): Транзакция переводит базу из одного корректного состояния в другое, не нарушая правил (например, типов данных).
  • Isolation (Изолированность): Параллельные транзакции не мешают друг другу.
  • Durability (Долговечность): Если система сообщила, что транзакция успешна, данные сохранены физически и не пропадут при сбое.
  • NoSQL: Гибкость и Масштабируемость

    С развитием веба и появлением Big Data, строгие правила SQL стали ограничением. Появились NoSQL (Not Only SQL) базы данных. Они отказываются от таблиц и жестких связей в пользу гибкости и скорости.

    Самый популярный тип NoSQL для веб-разработки — Документоориентированные СУБД (например, MongoDB).

    Документы вместо Таблиц

    В MongoDB данные хранятся в формате BSON (бинарный JSON). Вместо строк у нас «документы», а вместо таблиц — «коллекции».

    Пример документа в MongoDB:

    Главное отличие: в одной коллекции могут лежать документы с разной структурой. Это позволяет менять схему данных «на лету», не останавливая базу для миграций.

    Теорема CAP

    При выборе между SQL и NoSQL (особенно в распределенных системах) часто опираются на теорему CAP. Она гласит, что распределенная система может обладать только двумя из трех свойств одновременно:

    !Визуализация теоремы CAP, показывающая компромиссы при выборе архитектуры базы данных

  • Consistency (Согласованность): Во всех узлах системы данные одинаковы в любой момент времени.
  • Availability (Доступность): Любой запрос получает ответ (успех или ошибка), система не «виснет».
  • Partition Tolerance (Устойчивость к разделению): Система продолжает работать, даже если связь между серверами потеряна.
  • Реляционные базы обычно выбирают CA (или CP в кластерах), в то время как многие NoSQL решения ориентированы на AP (пусть данные будут немного устаревшими, зато сайт откроется быстро).

    Проектирование схемы данных

    Независимо от типа БД, проектирование начинается с определения сущностей и связей между ними. Существует три основных типа связей:

    1. Один к одному (One-to-One)

    Одна запись в таблице A соответствует одной записи в таблице B. Пример: Пользователь и его Паспортные данные. Обычно это разделяют, чтобы не грузить тяжелые данные паспорта каждый раз, когда нужно просто показать имя пользователя.

    2. Один ко многим (One-to-Many)

    Самый частый тип связи. Одна запись в таблице A связана с множеством записей в таблице B. Пример: Один Пользователь может сделать много Заказов. Но каждый Заказ принадлежит только одному Пользователю.

    В SQL это реализуется добавлением user_id (Foreign Key) в таблицу orders.

    3. Многие ко многим (Many-to-Many)

    Множество записей из таблицы A связаны с множеством записей из таблицы B. Пример: Студенты и Курсы. Один студент может ходить на много курсов, и на одном курсе учится много студентов.

    В реляционных базах такую связь нельзя создать напрямую. Для этого создается промежуточная таблица (Junction Table), например student_courses, которая содержит только пары ID:

    Где: * — множество записей в промежуточной таблице (связи). * — идентификатор студента. * — идентификатор курса. * — множество всех студентов. * — множество всех курсов.

    ORM: Мост между кодом и базой

    Писать «сырые» SQL-запросы внутри JavaScript-кода неудобно и небезопасно (риск SQL-инъекций). Поэтому Fullstack-разработчики используют ORM (Object-Relational Mapping).

    Популярные ORM для Node.js: Prisma, TypeORM, Sequelize, Mongoose (для MongoDB).

    ORM позволяет работать с базой данных как с обычными объектами и методами класса.

    Вместо SQL:

    Мы пишем JS-код (пример на Prisma):

    Это ускоряет разработку, обеспечивает типизацию (особенно с TypeScript) и автоматизирует миграции схемы.

    Что выбрать: SQL или NoSQL?

    Этот вопрос не имеет однозначного ответа, но есть общие рекомендации:

    Выбирайте SQL (PostgreSQL), если: * У вас сложная структура данных с множеством связей. * Важна целостность данных (финансы, заказы). * Вы заранее знаете схему данных.

    Выбирайте NoSQL (MongoDB), если: * Данные неструктурированы или часто меняется их структура. * Нужна очень высокая скорость записи и чтения простых данных (логи, аналитика, кеш). * Вы разрабатываете прототип и не хотите тратить время на проектирование схемы.

    Заключение

    База данных — это фундамент вашего приложения. Ошибки в проектировании схемы данных исправить гораздо сложнее, чем переписать код на фронтенде. В рамках нашего курса мы будем использовать PostgreSQL в связке с Prisma ORM, так как это «золотой стандарт» современной индустриальной разработки, обеспечивающий надежность и удобство.

    В следующем модуле мы перейдем к финальной стадии разработки — Деплою и DevOps, где научимся упаковывать наше приложение в контейнеры и размещать его на реальном сервере в интернете.

    5. DevOps для разработчика: Docker, CI/CD и развертывание проекта на сервере

    DevOps для разработчика: Docker, CI/CD и развертывание проекта на сервере

    Поздравляю! Вы прошли долгий путь. Вы научились верстать интерфейсы, создавать сложную логику на JavaScript, строить API на Node.js и проектировать базы данных. У вас есть потрясающее приложение... которое работает только на вашем ноутбуке.

    Но мир не увидит ваш продукт, пока он находится на localhost:3000. Чтобы превратить код в доступный сервис, нам нужно освоить последний, критически важный навык Fullstack-разработчика — DevOps (Development Operations).

    В этой финальной статье курса мы разберем, как упаковать приложение в контейнеры, автоматизировать его доставку и запустить на реальном сервере в интернете.

    Проблема: «А у меня работает»

    Классическая проблема разработки: вы написали код, протестировали его, и всё отлично. Вы отправляете его коллеге или на сервер, и там всё ломается. Причины могут быть разными:

    * Разные версии Node.js. * Разные операционные системы (Windows у вас, Linux на сервере). * Отсутствие системных библиотек.

    Раньше для решения этой проблемы использовали виртуальные машины (VM). Но они тяжелые: каждая VM несет в себе полноценную операционную систему. Решением стала контейнеризация.

    Docker: Судоходство в мире кода

    Docker — это платформа для разработки, доставки и запуска приложений в контейнерах.

    Представьте грузовой корабль. Ему всё равно, что внутри контейнера: автомобили, зерно или электроника. У контейнера есть стандартные размеры и крепления. Портовый кран (Docker) умеет грузить любые стандартные контейнеры.

    В программировании: * Image (Образ) — это чертеж или слепок вашего приложения. Он содержит код, библиотеки, зависимости и настройки среды. Образ неизменяем (read-only). * Container (Контейнер) — это запущенный экземпляр образа. Это изолированный процесс, который «думает», что работает на отдельном компьютере.

    Dockerfile

    Чтобы создать образ, мы пишем инструкцию — Dockerfile. Рассмотрим пример для нашего Node.js приложения:

    Теперь, где бы вы ни запустили этот контейнер (на Windows, Mac или Linux-сервере), внутри всегда будет Node.js 18 и ваше окружение будет идентичным.

    Оркестрация с Docker Compose

    Fullstack-приложение редко состоит из одного сервиса. Обычно у нас есть:

  • Backend (API)
  • Frontend (SPA)
  • Database (PostgreSQL/MongoDB)
  • Запускать их вручную отдельными командами неудобно. Для этого используется Docker Compose — инструмент для описания и запуска многоконтейнерных приложений.

    Мы описываем структуру в файле docker-compose.yml:

    Одной командой docker-compose up мы поднимаем всю инфраструктуру проекта.

    !Визуализация работы многоконтейнерного приложения, где каждый сервис изолирован, но может общаться с другими внутри сети Docker.

    CI/CD: Конвейер автоматизации

    Ручное копирование файлов на сервер через FTP — это прошлый век. Современная разработка использует CI/CD.

    * CI (Continuous Integration) — Непрерывная интеграция. Это процесс автоматической сборки и тестирования кода при каждом изменении (git push). Если тесты не прошли, код не попадет в основную ветку. * CD (Continuous Delivery / Deployment) — Непрерывная доставка. Автоматическое развертывание приложения на сервере после успешного прохождения CI.

    Популярные инструменты: GitHub Actions, GitLab CI, Jenkins.

    Типичный Pipeline (конвейер) выглядит так:

  • Checkout: Скачать код из репозитория.
  • Install: Установить зависимости (npm install).
  • Lint & Test: Проверить стиль кода и запустить тесты.
  • Build: Собрать Docker-образ.
  • Deploy: Отправить команду на сервер: «Скачай новый образ и перезапусти контейнер».
  • Оценка надежности системы

    Главная цель DevOps — обеспечить стабильность и доступность сервиса. В инженерии надежности (SRE) используется метрика Availability (Доступность), которая часто измеряется в «девятках» (например, 99.9%).

    Формула расчета доступности выглядит следующим образом:

    Где: * — доступность системы (Availability), доля времени, когда система работает исправно. * (Mean Time Between Failures) — среднее время между сбоями. Это время, пока система работает нормально. * (Mean Time To Repair) — среднее время восстановления после сбоя. Это время, которое требуется DevOps-инженеру, чтобы починить упавший сервер.

    Наша задача как инженеров — максимизировать (писать надежный код) и минимизировать (использовать Docker и CI/CD для быстрого отката или перезапуска).

    Развертывание на сервере (VPS)

    Для деплоя нам понадобится VPS (Virtual Private Server). Это удаленный компьютер, обычно на Linux (Ubuntu/Debian), к которому мы подключаемся через протокол SSH.

    Зачем нужен Nginx?

    Даже если ваше Node.js приложение умеет принимать запросы, выставлять его напрямую в интернет на 80-й порт — плохая идея. Перед приложением должен стоять Reverse Proxy (обратный прокси), например, Nginx.

    Задачи Nginx:

  • Раздача статики: HTML, CSS, картинки фронтенда Nginx отдает в разы быстрее, чем Node.js.
  • SSL/TLS: Шифрование трафика (HTTPS). Сертификаты настраиваются именно здесь.
  • Балансировка нагрузки: Если у вас запущено 3 экземпляра API, Nginx может распределять запросы между ними.
  • Безопасность: Скрытие реальной архитектуры сервера от внешнего мира.
  • !Схема работы Reverse Proxy: Nginx принимает все входящие запросы и распределяет их между статическими файлами и сервером приложений.

    Процесс деплоя (кратко)

  • Арендуем VPS, получаем IP-адрес.
  • Заходим по SSH: ssh root@123.45.67.89.
  • Устанавливаем Docker и Docker Compose на сервер.
  • Настраиваем GitHub Actions, чтобы при пуше в ветку main он собирал Docker-образ и отправлял его в Docker Hub (реестр образов).
  • GitHub Actions подключается к VPS по SSH и выполняет команду docker-compose pull && docker-compose up -d.
  • В результате ваше приложение обновляется автоматически через 2-3 минуты после того, как вы нажали кнопку «Commit».

    Заключение курса

    Мы прошли огромный путь в рамках курса «Профессия Fullstack-разработчик». Мы начали с простых HTML-тегов, изучили магию JavaScript, погрузились в реактивность Frontend-фреймворков, построили надежный Backend, спроектировали базу данных и, наконец, научились доставлять наш продукт пользователям через Docker и CI/CD.

    Быть Fullstack-разработчиком — значит видеть картину целиком. Вы не просто пишете функции, вы создаете системы. Технологии будут меняться, но инженерное мышление, которое вы развили, останется с вами навсегда.

    Удачи в создании ваших проектов!