Допущения в архитектуре

Думаю стоит сказать пару слов о том, как мы решили задачу написания интеграционных тестов. Опять-таки, это идея из прошлого проекта. В чём его суть? На нашем проекте мы используем CQS подход. Оперируем commands и queries. На уровне имплементации для этого мы используем MediatR (.net-девелоперы меня поймут, но для остальных в двух словах это такая библиотека в .net, которая как раз и позволяет удобно работать с этими абстракциями на основе intelligent dispatching и т.д.). Работая с этой библиотекой в основном оперируешь следующими понятиями/объектами: commands/queries и command/query handlers. Так вот, по всем теориям, подходам, примерам в GitHub в Clean Architecture и commands/queries и command/query handlers есть не что иное как UseCases, и соответственно должны относиться к Application Layer.
И вот здесь мы вносим допущение на уровне всего проекта, скажем так, своего рода архитектурный допуск. Мы делим UseCases между Domain Layer (туда уходят commands/queries и для нас они становятся “абстракциями UseCases-ов”) и Application Layer (туда уходят command/query handlers и для нас они становятся “реализациями UseCases-ов”). Понимаю, что от сказанного можно получить волну хейта от “ортодоксов” Clean Architecture. Я понимаю, какие отрицательные сайдэффекты мы получаем от этого, но они не настолько значительны, и в корне не ломают Clean Architecture. А вот плюсы следующие.

Плюсы такого подхода

От Domain Layer зависят все остальные слои приложения, но нас будут интересовать два - это Application и Infrastructure Layers. В Application расположены основные реализации UseCases (command/query handlers). А вот в Infrastructure у нас расположен прожект, выполняющий роль Client SDK этого сабдомена/микросервиса (паблишится этот SDK как nuget кстати).

И вот в этом проекте у нас вторые реализации UseCases (command/query handlers) только в основе которых уже не application logic, а просто HttpClient и запросы соответственно на API Layer этого же сабдомена/микросервиса. Т.е. По простому, что у нас получается? Получается Client SDK для микросервиса основанный на UseCases. Так а какие все-таки бенефиты?
Давайте представим, что вам надо написать интеграционный тест, и api-тест. При обычных подходах в api-тестах придется писать какую-то http надстройку, и собственно для того, чтобы протестировать один UseCase надо будет написать два теста. С нашим подходом - только один тест. Он отработает и по интеграционному сценарию и по api сценарию.
Ну конечно для этого нужен своего рода тумблер на тестовом окружении, который будет переключать DI Container, в составе которого будет либо application реализация UseCase, либо Client SDK реализация.

И по итогу девелоперы одним махом закрывают два теста, хотя пишут только один. И в результате на этапе разработки фичи мы получаем более качественно проверенные артефакты еще до деплоя их на тестовое окружение. Девелоперы могут более быстро покрывать сразу два подуровня в Test Pyramid, не дожидаясь какого бы то ни было респонса от QA на тестовом окружении, пока оно там задеплоится, пока тестировщик проверит и т.д. Ну а QA остается только E2E тесты. Ну и corner cases;)
Ну и само собой это все мне пришлось тоже рассказывать и показывать, писать документацию. Был для этого организован отдельный Tech Talk на весь проект. Но как результат все всё поняли. Тесты пишутся.

Да, unit-тесты мы тоже писали, но только для Domain Layer (т.е. покрывали Entities, Value Objects, Domain services). Все остальное покрывается интеграцией плюс api. Тесты ради тестов не писали;). Все эти примеры, опять-таки, были в темплейте.

Шестая часть будет совсем небольшой – о документации, которой Павел с коллегами сопроводили шаблон, и том, что дало его внедрение.