+7 495 981-01-85 + Become a customer
Services Cases Content-hub
Blog
Back

SOLID v real'nom mire: SRP bez arkhitekturnykh kosmoletov SOLID in the Real World: SRP Without Architectural Spaceplanes

#Development 26 march 2026
Рано или поздно каждый разработчик сталкивается с необходимостью изучить принципы SOLID. Интернет полон теоретических статей с абстрактными примерами — треугольниками, фигурами и системами заказов. В таких примерах всё выглядит красиво. Но когда дело доходит до продакшен-кода, возникает логичный вопрос: как применять эти принципы на практике и не превратить проект в архитектурный космолет? Разбираемся.
Дисклеймер: статья предназначена для новичков, которые только познают все принципы хорошего кода.
Проблема в том, что при написании кода по SOLID многие забывают о других принципах разработки. Например:
  • KISS;
  • DRY;
  • YAGNI.
В реальной разработке эти принципы иногда конфликтуют друг с другом. Например, пытаясь сделать код «гибким» и «расширяемым», можно легко нарушить YAGNI и добавить абстракции, которые не нужны ни сейчас, ни в обозримом будущем.
На практике это выглядит так: простой код на 2–3 класса превращается в непонятный набор из 10+ классов, интерфейсов, фабрик и адаптеров. И самое интересное — бизнес об этом даже не просил.

Проблема №1. Нарушение YAGNI

Одна из самых частых проблем, которые я вижу на практике — нарушение принципа YAGNI. Разработчики начинают проектировать систему так, будто заранее знают, как будет развиваться бизнес. Но в реальности этого не знает никто. Даже сам бизнес.
В результате появляются:


  • абстракции «на будущее»;
  • интерфейсы без реальных альтернативных реализаций;
  • архитектурные конструкции, которые никогда не используются.

Код становится сложнее, но не гибче.

Проблема №2. Охота за интерфейсами

Другая распространенная проблема — создание десятков интерфейсов в погоне за принципом Interface Segregation. Сам принцип отличный. Но иногда он превращается в соревнование: кто создаст больше интерфейсов с одним методом.
Формально всё красиво. Практически — оверхед.
И это только часть проблем, остальные разберем в отдельных статьях.

Что на самом деле имели в виду авторы SOLID

Многие разработчики в целом неправильно понимают, какой смысл закладывали создатели SOLID. Возьмем самый известный принцип — SRP. Чаще всего его формулируют так:
У модуля должна быть одна и только одна причина для изменения.
Но сам Роберт Мартин, создатель методологии ООП, сформулировал его немного точнее:
Модуль должен быть ответственен перед одним и только одним актором.
И это важная деталь. SRP — это не про количество методов в классе и не про то, чтобы каждый класс делал «что-то одно». SRP — это про то, кто инициирует изменения.

Кейс: система отзывов

Рассмотрим простой продакшен-кейс. Есть продукт с несколькими каналами:
  • Сайт.
  • Мобильное приложение.
  • PWA.
Нужно реализовать систему отзывов:
  • Пользователь может оставить отзыв.
  • Отзывы можно посмотреть в админке.
  • Раз в неделю формируется отчет в XLSX и отправляется на почту.
На первый взгляд задача довольно простая. И вот здесь момент, когда многие разработчики достают из кобуры SOLID и начинают стрелять абстракциями во всё, что движется. Иногда даже в то, что не движется.

Как появляется архитектурный космолет

Очень часто разработчик начинает рассуждать примерно так:
  • А вдруг отзывы будут храниться не только в БД?
  • А вдруг их будут получать из внешнего сервиса?
  • А вдруг отчет будут отправлять не только на почту?
  • А вдруг появится еще пять каналов?
И тут начинается инженерная фантастика. В коде появляются примерно такие вещи:
                    interface FeedbackDataSource
{
    public function getFeedback(): array;
}
class DatabaseFeedbackSource implements FeedbackDataSource
{
    public function getFeedback(): array
    {
        // получение из БД
    }
}
interface FeedbackSender
{
    public function send(Report $report): void;
}
class EmailFeedbackSender implements FeedbackSender
{
    public function send(Report $report): void
    {
        // отправка письма
    }
}
interface FeedbackChannel
{
    public function collect(FeedbackDTO $dto): void;
}
                
Потом добавляются реализации:
  • WebsiteFeedbackChannel;
  • MobileAppFeedbackChannel;
  • PwaFeedbackChannel;
А затем появляются:
  • FeedbackRepositoryInterface;
  • FeedbackExporterInterface;
  • FeedbackProviderFactory;
  • AbstractFeedbackExporter.
И где-то на этом этапе простая фича превращается в архитектурный симулятор NASA. Самое интересное — ни одной из этих абстракций бизнес не просил. Мы просто начали защищаться от гипотетического будущего.
Схематично это может выглядеть вот так:
Фотография

  • Команда: продакт-менеджер, менеджер проекта, аналитик, арт-директор, дизайнер и 3 программиста;

В чем проблема такого подхода

На архитектурной диаграмме всё выглядит красиво: гибко и расширяемо. И соответствует SOLID. Но в реальности мы получаем:
  • десятки классов;
  • сложную навигацию по коду;
  • абстракции без реальных реализаций;
  • лишнюю когнитивную нагрузку для команды.
И самое главное: мы оптимизировали архитектуру под будущее, которое может никогда не наступить. Это прямое нарушение YAGNI.

Но и игнорировать SRP нельзя

Есть и другая крайность.Можно сказать: «Да ну этот SOLID. Сделаем один `FeedbackService` на 800 строк и поедем дальше». Это тоже плохая идея. Чтобы принять разумное решение, нужно задать главный вопрос SRP: кто является актором изменений? Точно не интерфейсы. Не абстракции. И не «вдруг когда-нибудь».

Кто акторы в нашем кейсе

В реальном продукте ситуация может выглядеть примерно так. Продакт сайта хочет показывать форму после покупки и собирать имейлы. Продакт мобильного приложения хочет показывать NPS после третьего запуска, а имейлы ему не нужны. Продакт PWA хочет максимально короткую форму
Здесь важно заметить одну вещь. Каналы могут иметь разные правила сбора отзывов. А значит, изменения могут происходить независимо:
  • правила сайта могут поменяться;
  • правила мобильного приложения — нет;
  • PWA может вообще отключить сбор отзывов.
Вот здесь появляются разные акторы, а значит, и разные причины для изменений. И это уже сигнал для применения SRP.

Как может выглядеть разумная архитектура

Без космолета, но и без `God Object`.
                    class FeedbackService
{
    // ответственность: сохранение отзывов
    public function __construct(
        private FeedbackRepository $repository
    ) {}
    public function submit(Feedback $feedback): void
    {
        $this->repository->save($feedback);
    }
}
class FeedbackRepository
{
    // ответственность: хранение отзывов
    // источник данных один — база данных
    public function save(Feedback $feedback): void
    {
        // запись в БД
    }
    public function getAll(): array
    {
        // получение отзывов
    }
}
class FeedbackReportService
{
    // ответственность: формирование и отправка отчёта
    public function __construct(
        private FeedbackRepository $repository,
        private ReportMailer $mailer
    ) {}
    public function sendWeeklyReport(): void
    {
        $feedback = $this->repository->getAll();
        $report = FeedbackXlsxReport::generate($feedback);
        $this->mailer->send($report);
    }
}
                
  • Команда: продакт-менеджер, менеджер проекта, аналитик, арт-директор, дизайнер и 3 программиста;

Что мы сознательно не делаем

Мы не добавляем:
  • FeedbackDataSourceInterface;
  • FeedbackSenderInterface;
  • FeedbackStorageAdapter;
  • ExternalFeedbackProvider.
Мы не делаем этого, потому что отзывы хранятся в БД, отчет отправляется на имейл, а других требований нет (и скорее всего, не будет). Если когда-нибудь появится внешний сервис, добавить адаптер будет не сложно.
А пока код остается простым, понятным и поддерживаемым. И кроме того, закрывает все потребности бизнеса. Куда бизнес свернет через месяц, неизвестно никому. Поэтому предусмотреть всё заранее в любом случае не получится.
Схематично это может выглядеть так:
Фотография

Фотография

Вывод

SRP — это не лицензия на бесконечную декомпозицию и уж точно не приглашение писать архитектуру на случай апокалипсиса. Разделять код имеет смысл только в таких случаях:
  • есть разные причины для изменений;
  • есть разные акторы;
  • части системы могут меняться независимо.
Всё остальное — преждевременная архитектура, а преждевременная архитектура зачастую означает лишний код… Который потом придется поддерживать. Всегда важно критически мыслить. Понимать, где и как нужно заложить возможности расширение, а где это является оверхедом и будет лежать мертвым грузом в проекте.
А поддержка ненужного кода — занятие примерно такое же увлекательное, как чтение логов в пятницу вечером.

Reach out to us
to start discussing your project

Content-hub

0 / 0