Однозначного рецепта нет.
Самый простой случай: вы строите проект на основе какого - либо фреймворка, и подтягиваете другие возможности, прежде всего БД, может быть, редиску или кролика, в качестве вспомогательных элементов для реализации задач проекта. Здесь все просто - фреймворк определяет размещение, количество и наполнение файлов. Если речь о web фреймворках, и количество страниц заметное, то, не возбраняется разложить ендпойнты по пакетам, согласно их логической близости.
Случай посложнее - монолитный сервис. Здесь в дело вступает декомпозиция по зонам ответственности, авторизация и регистрация, контроль прав, работа с данными проекта, обработка запросов, мониторинг, статистика, логгирование и так далее. Здесь, я, обычно, разделяю код по пакетам, чтобы не смешивать задачи, и сохранять его "обозримость".
Если, например, вы рассматриваете микросервисный проект, где подбираете инфраструктуру, и с помощью отдельных процессов выстраиваете взаимодействие между элементами, то, каждый микросервис, по сути, является отдельным приложением, которое может разделять, а может и нет, кодовую базу предметной области с другими.
Коллеги вспомнили о структурных паттернах, но, позволю себе обратить внимание на то, что данные шаблоны не регламентируют разделение кода по файлам. В них речь идет исключительно о способах решать конкретные проблемы.
Если смотреть на вопрос со стороны ТЗ, то, я стараюсь выделять минимальные смысловые части, и реализовывать их, как сочту правильным/удобным. Например, написано: работа пользователя с данными возможна только после авторизации, новые пользователи обязаны пройти процедуру регистрации. Из этого вытекает такая последовательность реализации: создаем миграцию для создания таблицы пользователей, создаем процедуру регистрации, создаем процедуру проверки данных авторизации, организуем хранение информации об авторизованных пользователях "кто онлайн". Миграции, как правило, хранятся пофайлово, манипуляция с учетками, может, относится к работе с объектом "пользователь", контроль прав может лежать в отдельном пакете, и подключаться по необходимости.
На мой взгляд, получается, как-то так.