TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • flag Russian
    Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
.NET sh blog

27 Feb 2024, 23:01

Открыть в Telegram Поделиться Пожаловаться

Clean Architecture + Vertical Slices + DDD

Такая комбинация показала себя хорошо на практике, поэтому я считаю её наиболее удачной для реализации сложной предметной области.
Тема достаточно обширная, поэтому будет состоять из множества блоков :) Начнем.

Из чистой архитектуры я беру в основном Dependency Rule, UseCases и некоторые идеи из остальных.

Для изоляции разных уровней абстракций выделяю следующие сборки:
Api
↓
Infrastructure
↓
Application
↓
Domain

В дополнение к Api еще выделяется сборка BackgroundJobs, которая полезна когда требуется отделить асинхронную или фоновую работу чтобы масштабировать независимо от API.

Рассмотрим что в каждой сборке находится

в Api/BackgroundJobs:

* Authentication/Authorization
* DI Composition Root
* HealthChecks
* ProblemDetails
* Swagger
* Middlewares
* Controllers, Minimal API Endpoints
* Consumer, Producer
* Outbox
* И т.п.

Остановимся подробнее на основных вещах:

* Controllers & Minimal API Endpoints

Здесь начинаем применять Vertical Slices и изолируем фичи друг от друга структурой папок и неймспейсами:

Api
ProblemDetails
Swagger
HealthChecks
...
Features
WithdrawMoney
WithdrawMoneyController.cs
WithdrawMoneyEndpoint.cs
WithdrawMoneyModel.cs
WithdrawMoneyModelValidator.cs
WithdrawMoneyModelExample.cs
WithdrawMoneyResponseModel.cs
WithdrawMoneyResponseModelExample.cs
OtherFeatureName
...

Заметьте, что все что связано с конкретной фичей располагается в одной папке внутри папки Features. Обращаться к соседним фичам категорически нельзя (для этого можно сделать архитектурный тест).

Модели объявляются для каждой фичи свои и используют только примитивы или другие модели этой фичи. Это позволяет независимо развивать фичи и избегать неожиданных поломок из-за изменений в другой фиче.

Так как эти модели предназначены только для десериализации данных из методов контроллера, то мы можем им придать любую форму для того чтобы сделать максимально удобный API.

Валидатор проверит поля на корректность и обязательность их заполнения. А файлы с суффиксом Example - это примеры моделей для сваггера для наглядности.

Если Вам удобно, можно не делить всё это на отдельные файлы и расположить всё в одном, тут уж дело вкуса и соглашений в команде. Здесь я их все отделил для наглядности.

Еще хочется отметить, что здесь встречается одна из самых частых ошибок проектирования, когда объявляется DTO в слое Application (хуже в Domain или еще хуже - используется доменная модель в качестве DTO)
и эта модель используется в контроллере для десериализации данных и последующей передачи в нижележащие слои.

И начинаются настоящие танцы с бубном - пишутся кастомные ModelBinders, навешиваются атрибуты [FromBody], [FromForm], [FromQuery] и т.п. в полях DTO чтобы все замапилось как надо, так как часть параметров в QueryParams, часть в теле, часть в пути, часть заголовках, а UserId лежит где-нибудь в HttpContext.
Со временем требования меняются, код рефакторится, приходят новые люди в команду со своими подходами.. и эти модели тоже могут внезапно измениться, а контракты API - нет и в итоге сломается маппинг.

Тут на лицо нарушение SRP, а также протекание абстракций. Так как эти DTO объявлены в Application/Domain. А эти слои не должны знать о внешних слоях (особенно про AspNetCore, контроллеры и прочее).

* Consumer, Producer

Здесь в целом та же самая история, единственное - для сообщений из шины часто используют proto/avro и генерируют из них DTO.
Это очень удобно и решает сразу большинство описанных выше проблем - модели генерируются под конкретный Consumer/Producer,
а также они не ссылаются на другие модели из других фич или модели других слоёв. Являются полностью автономными.
Также, расположив эти модели на внешнем слое (Api/BackgroundJobs), мы не позволяем к ним обращаться из слоёв ниже. Если бы они объявлялись в Application или Domain, то программисты часто проигрывают лени
и MQ модели просачиваются сквозь все слои к которым они имеют доступ и потом их очень сложно оттуда убрать.

Продолжение следует...

74 0 0 13 7
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot