Продолжаю серию постов о том, как пишу собственный блог.
После того, как минимальный backend был реализован, я задумался о том, как развивать систему дальше, чтобы не повторить прошлые ошибки и не загнать себя в архитектурный тупик.
Идею держать всё в одном Django-приложении я сразу отверг. На старте это удобно: один репозиторий, один деплой, минимум инфраструктуры, есть инструменты, которые позволяют реализовать frontend на шаблонах. Но как только появляется полноценный frontend и нормальные CI/CD процессы — границы начинают размываться, и система становится сложнее, чем должна быть.
Проекты, с которыми я работал, обычно не выделяли инфраструктуру в отдельный репозиторий. Хотя в одном из них эти процессы всё-таки были обособлены, и мне это понравилось. Поэтому я решил в качестве эксперимента попробовать реализовать новую для себя схему:
- backend — отдельный сервис с API
- frontend — отдельное приложение, которое работает с этим API
- infra — отдельный слой, отвечающий за деплой, окружение и сборку
Это не попытка уйти в микросервисы, а разделение проекта на зоны ответственности. Каждая часть системы отвечает за свою область и не тянет на себя лишнего. Если бы я оставил всё в одном репозитории, то любое изменение тянуло бы за собой всё сразу — от API до деплоя. В какой-то момент это начинает замедлять разработку сильнее, чем кажется на старте.
Такое решение даёт ощутимое преимущество: части системы перестали зависеть друг от друга. Backend можно менять, не думая о UI, frontend — развивать отдельно, а инфраструктура теперь живёт отдельно и не связана напрямую с кодом приложения. Однако у всего есть цена. Появляется необходимость явно поддерживать API-контракт. Ошибки на стыке сервисов становятся более вероятными, а деплой усложняется за счёт увеличения количества отдельных частей системы.
Моя практика показывает, что плюсов больше, чем минусов — прежде всего за счёт контроля связности. Явные границы через API фиксируют точки взаимодействия: зависимости перестают быть скрытыми и становятся управляемыми. Любое изменение проходит через контракт, а значит становится предсказуемым — понятно, где и что может сломаться.
По сути, это перевод сложности из неявных связей внутри кода в явные интерфейсы между частями системы. Это упрощает развитие: можно менять отдельные компоненты, не затрагивая остальные, пока соблюдается контракт.
В итоге это не про сложную архитектуру, а про управляемость и предсказуемость системы.