Курс

Архитектура программных систем

Как проектируют серверные системы независимо от языка и фреймворка: что делает границу модуля хорошей, когда монолит разумнее микросервисов и как делить систему на сервисы; синхронная и асинхронная интеграция через очереди и брокеры, гарантии доставки и паттерн outbox; масштабирование, балансировка нагрузки и компромиссы согласованности из теоремы CAP; доменно-ориентированное проектирование и разделение чтения и записи. Цель — модель решений и их цены, на которую ложится реализация на любом стеке. Примеры на C#, принципы нейтральны.

5модулей21урок

Программа

Что внутри курса

  1. 01

    Модульная архитектура

    3 урока

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

    1. Связанность и связность: критерий границыКачество архитектуры определяют две силы: связанность (насколько модули зависят друг от друга) и связность (насколько элементы внутри модуля относятся к одной задаче). Цель — слабая связанность и высокая связность. Урок показывает, как эти две силы задают, где провести границу между модулями, и почему граница в неверном месте делает систему хрупкой.
    2. Организация кода: по слоям или по фичамКод одного и того же приложения можно разложить по техническим слоям (контроллеры, сервисы, репозитории отдельными группами) или по фичам — вертикальными срезами, где всё для одной возможности лежит вместе. Урок сравнивает оба подхода через связанность и связность и показывает, почему слоистая структура часто прячет высокую связанность по фичам.
    3. Модульный монолит как стартовая архитектураМодульный монолит — это одно приложение, разделённое внутри на модули с явными границами и контрактами, через которые они общаются. Урок показывает, почему это разумная архитектура по умолчанию: она даёт границы предметной области без сетевой и операционной цены распределённой системы, и почему модуль с чёткой границей легче выделить в сервис позже, когда это понадобится.
  2. 02

    Очереди и интеграция

    4 урока

    Как сервисы обмениваются данными, не завися друг от друга во времени: синхронный вызов против асинхронного сообщения, брокеры RabbitMQ и Kafka и их разные модели, гарантии доставки и идемпотентность потребителя, паттерн outbox против проблемы двойной записи. Модуль строит модель надёжной интеграции, в которой отказ или задержка одного сервиса не валит остальные.

    1. Синхронная и асинхронная интеграцияСервисы общаются двумя способами: синхронным вызовом, где вызывающий ждёт ответа и зависит от доступности вызываемого прямо сейчас, и асинхронным сообщением через очередь, где отправитель кладёт сообщение и продолжает работу. Урок разбирает связанность во времени как главное различие и показывает, когда блокирующий вызов уместен, а когда он превращает задержку в каскадный отказ.
    2. Брокеры: RabbitMQ против KafkaRabbitMQ и Kafka оба передают сообщения, но устроены по-разному: RabbitMQ — брокер с очередями и маршрутизацией через exchange, который доставляет сообщение и забывает его; Kafka — durable лог из партиций, где сообщения хранятся и читаются по смещению, а потребитель может перечитать их заново. Урок объясняет эту разницу в модели и какие задачи решает каждый.
    3. Гарантии доставки и идемпотентностьСеть теряет и дублирует сообщения, поэтому брокеры дают разные гарантии: at-most-once (можно потерять), at-least-once (можно получить дубль), exactly-once (трудна и дорога). Урок показывает, почему at-least-once — практичный дефолт, а exactly-once почти всегда достигается не магией брокера, а идемпотентным потребителем, который распознаёт и отбрасывает повторную обработку.
    4. Проблема двойной записи и паттерн outboxКогда обработчик должен и записать в базу, и отправить сообщение, между двумя операциями нет общей транзакции: упасть можно после записи, но до отправки — и состояние разойдётся. Урок разбирает эту проблему двойной записи и паттерн transactional outbox: сообщение пишут в ту же базу одной транзакцией, а отдельный процесс-релей надёжно дочитывает и публикует его в брокер.
  3. 03

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

    5 уроков

    Как система держит нагрузку: масштабирование вверх и вширь и почему без отсутствия состояния второе невозможно, балансировка нагрузки, слои кэширования и реплики на чтение, компромиссы согласованности из теоремы CAP и разбиение данных по узлам. Модуль связывает backend-навыки с проектированием системы целиком и её цены под ростом запросов.

    1. Масштабирование и отсутствие состоянияНагрузку держат двумя способами: масштабированием вверх (более мощная машина) и вширь (больше машин). Второе дешевле и надёжнее, но работает, только если сервис не хранит состояние между запросами — иначе следующий запрос попадёт не на ту машину. Урок показывает, почему отсутствие состояния — условие горизонтального масштабирования, и куда тогда выносят сессию и данные.
    2. Балансировка нагрузкиКогда сервис работает на нескольких машинах, перед ними ставят балансировщик, который распределяет запросы. Урок разбирает, как он раскладывает нагрузку и проверяет здоровье инстансов через пробы, почему привязка сессии к конкретной машине ломает равномерность и отказоустойчивость, и как балансировщик связан с выводом нездоровых инстансов из ротации.
    3. Кэш и реплики на чтениеКогда база не справляется с чтением, нагрузку снимают двумя способами: кэшем, который держит частые ответы ближе к приложению, и репликами на чтение, которые копируют базу для запросов на чтение. Урок разбирает, на каких слоях кэшируют и какой ценой, чем реплика отличается от кэша и почему оба вводят отставание данных, с которым приходится считаться.
    4. Теорема CAP и согласованностьВ распределённой системе сеть рано или поздно разрывается, и теорема CAP говорит: при разрыве приходится выбирать между согласованностью (все видят одни данные) и доступностью (система отвечает). Урок разбирает, почему «выбрать всё» нельзя, чем сильная согласованность отличается от итоговой и почему многие системы сознательно выбирают доступность и итоговую согласованность.
    5. Разбиение и шардирование данныхКогда данные не помещаются на один узел или одна база не тянет запись, их разбивают по узлам — шардируют по ключу. Урок разбирает, как выбор ключа разбиения определяет равномерность нагрузки, почему неудачный ключ создаёт горячие точки, куда уходит вся нагрузка, и какой ценой шардирование усложняет запросы, затрагивающие несколько шардов.
  4. 04

    Микросервисы

    5 уроков

    Разбиение системы на отдельно разворачиваемые сервисы: когда и по какому принципу делить, как сервисы общаются (REST против gRPC, синхронно против асинхронно), кто координирует многошаговый процесс и как держать данные согласованными без общей транзакции через паттерн Saga. Модуль показывает, что микросервисы решают проблему масштаба команд ценой распределённости.

    1. От монолита к микросервисам: когда и зачемМикросервисы решают не техническую, а организационную проблему: они позволяют командам разворачивать части системы независимо. Урок показывает, по какому принципу проводят границы сервисов (по предметной области, как у модулей монолита), какую цену приносит распределённость — сеть, согласованность, эксплуатация — и почему дробить монолит без этой нужды делает только хуже.
    2. Межсервисное общение: REST и gRPCСервисы общаются синхронно или асинхронно, а для синхронного вызова выбирают между REST поверх JSON и gRPC поверх HTTP/2 с бинарным форматом Protobuf и строгой схемой. Урок сравнивает их по производительности, контракту и применимости, показывает, где gRPC выигрывает на частых межсервисных вызовах, и почему выбор протокола вторичен по отношению к выбору синхронность/асинхронность.
    3. Оркестрация против хореографииМногошаговый процесс через несколько сервисов координируют двумя способами: оркестрацией, где центральный координатор командует шагами, и хореографией, где сервисы реагируют на события друг друга без дирижёра. Урок разбирает компромисс между ними — явный контроль и единая точка против слабой связанности и размазанной логики — и где какой стиль уместнее.
    4. Распределённые транзакции и SagaКогда бизнес-операция затрагивает несколько сервисов с собственными базами, общей транзакции нет, а двухфазный коммит (2PC) плохо масштабируется и блокирует. Урок разбирает паттерн Saga: операцию разбивают на последовательность локальных транзакций, и при сбое на шаге запускают компенсирующие действия, откатывающие уже сделанное — согласованность достигается не атомарностью, а компенсацией.
    5. База данных на сервисМикросервисы держат данные раздельно: у каждого своя база, и чужую он трогает только через API владельца, а не напрямую. Урок объясняет, почему общая база связывает сервисы обратно в монолит, какой ценой идёт раздельное хранение — дублирование данных и итоговая согласованность вместо мгновенной — и почему это плата за независимость развёртывания.
  5. 05

    DDD и CQRS

    4 урока

    Доменно-ориентированное проектирование как способ держать сложную предметную область под контролем: единый язык, сущности и объекты-значения, агрегаты как границы согласованности и ограниченные контексты; и CQRS — разделение моделей чтения и записи. Модуль показывает мощные приёмы для сложных доменов и честно — почему как архитектура по умолчанию они вредят.

    1. Строительные блоки DDDДоменно-ориентированное проектирование начинается с языка: код и бизнес говорят одними терминами (единый язык). Урок разбирает базовые блоки — сущность (объект с идентичностью во времени) против объекта-значения (определяется только своими полями) и ограниченный контекст, внутри которого термин имеет одно точное значение, и показывает, зачем эти различения сложному домену.
    2. Агрегаты и границы согласованностиАгрегат — это группа объектов, которая меняется как одно целое через единственную точку входа, корень агрегата, и образует границу согласованности и транзакции. Урок показывает, почему все инварианты внутри агрегата держат строго и атомарно, а между агрегатами допускают итоговую согласованность, и как размер агрегата влияет на конкуренцию за запись и масштабируемость.
    3. CQRS: разделение чтения и записиCQRS разделяет модель, которой пишут данные, и модель, которой их читают, потому что у записи и чтения разные требования: запись хранит инварианты, чтение оптимизировано под запросы. Урок показывает, что это даёт — независимое масштабирование и удобные модели чтения — и честно, почему для простого CRUD CQRS добавляет сложность без выгоды и применять его стоит по реальной нужде.
    4. Введение в event sourcingОбычно система хранит текущее состояние и перезаписывает его. Event sourcing хранит вместо этого последовательность событий, а текущее состояние выводит, проигрывая их заново. Урок разбирает, что это даёт — полную историю изменений и аудит — и какой ценой: сложность, версионирование событий и связь с CQRS, где модель чтения строится из потока событий. Приём для немногих случаев, не дефолт.

Готовьтесь по структуре, а не вслепую

Откройте курс в приложении и закрепляйте темы в тренажёре вопросов с AI-разбором.

Архитектура программных систем: подготовка к собеседованию — JobJump