Нормально ли с точки зрения SOLID проектировать сервис, который взаимодействует с базой, мапит DTO и вызывает Kafka
Что проверяют
Понимаете ли вы принцип единственной ответственности как «одна причина для изменения», а не как «один класс делает одно действие». Вопрос провокационный: интервьюер ждёт, скажете ли вы автоматически «нарушает SOLID, надо дробить», или разберёте, где здесь оркестрация, а где смешение уровней.
Как отвечать
Нормально, если сервис оркестрирует, а не реализует. Слой приложения имеет право знать сценарий целиком: получить данные, собрать ответ, отправить событие. Ненормально, когда внутри него лежит SQL, ручная сборка сообщения и настройки продюсера.
Как это выглядит на практике:
- работа с базой в репозитории, отправка событий в отдельном компоненте за своим интерфейсом, преобразование в мапперах;
- сервис зависит от абстракции, а не от
KafkaTemplateнапрямую. Тогда принцип инверсии зависимостей соблюдён и тест пишется с заглушкой; - DTO не протекает в репозиторий, а сущность не уезжает в контроллер.
Дальше скажите то, чего интервьюер обычно и ждёт: база и брокер это два разных ресурса, и @Transactional их не объединяет. Если событие обязано соответствовать состоянию базы, нужен исходящий журнал: пишем событие в ту же транзакцию, отправляет его отдельный процесс.
Где спотыкаются
Перечисляют пять принципов по учебнику и не отвечают на заданный вопрос. Спросили про конкретный дизайн, а в ответ идёт определение принципа подстановки.
Второе: дробят до абсурда, заводя класс на каждое действие. Получается слой делегирования, где каждый метод вызывает ровно один чужой метод, а прочитать сценарий целиком негде.
Третье: обещают атомарность базы и брокера. Отправка в Kafka внутри транзакции даёт и потерянные события при откате, и дубликаты при повторах, поэтому про идемпотентность потребителя нужно сказать самому.