7-9 -- уже сложный случай (порт/адаптер, оркестрация...), и как правило тут паттерны выражены аннотациями/конфигом, а не "ручным" кодом.
Ну например, этот ваш CRUD будет таким паттерном:
controller/endpoint -> mapper (dto, entity) -> use case (service) -> repo -> dbСквозной путь запроса/кейса -- максимум 10–12 логических шагов (включая нутро фреймворка!). Переходов между bounded contexts при этом -- три максимум, хопов DI-зависимостей -- пять максимум. и т.д.
на эту тему в СильныхИдеях на днях выложу материал
"Проектирование "снизу вверх" через рефакторинг (и при чём тут зависимости)"
Вообще, если регулярно получаете >7 ручных слоёв на типовую фичу -- упрощайте модель, ищите подходящую библиотеку, пилите DSL...
...Ладно, спалю базу: хорошая архитектура -- это не больше слоёв/глубже комбинаций паттернов, а меньше "случайной" сложности при сохранении явных инвариантов. Держим сквозной путь ≤10-12 и автоматизируем всё, что не домен.
"Идеальность" архитектуры не связана с глубиной вложенности паттернов. Она растет, как считается в программной инженерии, пропорционально ясности сценариев по BDD, инкапсуляции и low coupling (хотя я писал целый сериал, почему low coupling -- это больше абстракция, чем практика).
Ставьте на чистую доменную модель с явными границами, конвейеры политик и Functional Core/Imperative Shell (максимум логики в чистом ядре, "эффекты" на границах, причём "эффектный" слой должен быть как можно тоньше).
+ Рекомендую capability-ориентированный дизайн: это когда глупенький пишет тупой CRUD...
user.setStatus("ACTIVE");
userRepository.save(user);
// да, но с какой целью??...а умненький явно выражает бизнес-намерение:
userActivationService.activateUser(userId);
=
Далее выявляем порты -- интерфейсы на границе домена, которая таким образом задаётся явно. Затем определяем соответствующую алгебру -- формальную сигнатуру операций на этой границе (что домен требует от внешнего мира? БД, REST, брокер?).
Алгебра в данном контексте -- это абстрактный набор операций предметной области (подписание платежа, чтение корзины, генерация UUID), заданных как чистая спецификация (а уж под каким эффектом это исполняется -- не важно). Мой гайд по абстрактным типам данных в ООП в помощь, а в ФП это что-то типа Tagless Final/Free Algebras (что тоже разбираю в соответствующем гайде :).
Ну и далее "интерпретатор" реализует алгебру поверх конкретных эффектов/технологий (DB/HTTP/...)...
=
Учимся по BDD распознавать (неявные) вариации и ограничения в ТЗ и маппить их на паттерны. По моим оценкам, таких паттернов около 50, ну может под сотню. Готовой теории, которая бы их охватывала и систематизировала, у меня нету, поэтому сперва их надо сформулировать и классифицировать, затем добавить функциональное описание, затем формальное, а потом и сама теория родится, полагаю, естественно и легко.
