Зачем разделять контроллер, сервис и репозиторий и что живёт в каждом слое?
Контроллер отвечает за протокол: разбор запроса, коды ответов, сериализация. Сервис содержит бизнес-правила, порядок шагов и границу транзакции. Репозиторий знает про хранилище и возвращает объекты домена, а не строки таблиц. Зависимости идут только сверху вниз: контроллер знает про сервис, сервис про репозиторий, обратно никто. Смысл именно в этом направлении: сценарий можно вызвать не только из HTTP — из фоновой задачи, из консольной команды, из обработчика события — и протестировать без веб-сервера и базы, подставив фейковое хранилище. Правило, написанное в контроллере, ломает и то и другое: оно становится доступно только через HTTP. Платите числом файлов и переходов, поэтому на чистом CRUD слои не заводят — их вводят там, где есть что защищать.