- Создайте fork репозитория
- Начните с ветки step-1, прочитайте пункт 1 этого readme
- Прочтиайте второй пункт этого readme. Обратите внимание на подпункты "задача" и "что делаем"
- Переключитесь на ветку step-2, посмотрите на результат рефакторинга
- Вернитесь на ветку step-1 и проведите рефакторинг
- Повторите процесс для веток step-2 - step-9
- Каждый следующий шаг более сложен и дорог в реализации. В реальных проектах используйте инструменты там, где действительно необходимо.
- Толстые Тупые Уродливые Контроллеры
- Tutorial: Implement CRUD Functionality - ASP.NET MVC with EF Core
- Razor Pages with Entity Framework Core in ASP.NET Core
- Просто
- Лучшая возможная производительность
- Затраты на поддержку растут с количеством кода; по закону брукса разработка в какой-то момент остановится
- Не допустить превращения кодовой базы в Big Ball of Mud
- CRUD VS Task-Based UI
- нарушение SRP
- смешение бизнес логики и инфраструктурного кода
- затруднено повторное использование
Services
,Helpers
,Managers
,...
повышается связность по мере роста проекта, в итоге имеется тенденция к Big Ball of Mud- Cross-cutting concerns
- Затруднен Feature Toggle и рефакторинг
- Конфликты при мердже, не соблуюдается принцип OCP
- Потеря контекста при навигации по файлам
- Реализовать вертикальные слайсы без использования внешних библиотек, вроде
MediatR
илиSimpleInjector
- Или применить одну из них :)
- Создать папку Features
- Внтури создать папку на каждую "фичу"
- Разложить
DTO
,Handler
,Controller
по фича-папкам - Использовать базовый класс
GetEnumerableQueryHandlerBase
для стека чтения - Автоматически вызывать
SaveChanges
в стеке записи
- CQRS. Факты и заблуждения
- Meanwhile... on the command side of my architecture
- Meanwhile... on the query side of my architecture
- Report progress and cancel long running queries
- DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together
- Меньше конфликтов
- Больше добавления и удаления кода, меньше редактирования
- Базовые хендлеры, меньше бойлерплейта
- Обобщенная обработка Cross-cutting concerns
- Больше инфраструктурного кода
- Непривычно
- Циклические зависимости
- Когнитивная сложность
- Затруднен рефакторинг
- Время компиляции, запуска
- Убрать явную зависимость от конкретной ORM
Разделить проект на модули (отдельные сборки)
- Создать сборку на для каждого модуля
- Перенести фича-папки в соответствущие сборки
- Feature modules
- Как запустить MVP и не превратить его в техдолг
- Table splitting
- Domain-driven design: рецепт для прагматика
- Зависимости инвертированы, нет циклических зависимостей
- Нет зависимостей от конкретной ORM в домене
- Видны зависимости между модулями
- Больше абстраций
- Больше инфраструктурного кода
- Нужны
Domain Events
- Борьба с NRE, частичной инициализацией
- Код определяет допустимые и недопустимые состояния объектов
- Ипользовать базоый класс
EntityBase
- Создать специализированные конструкторы, соблюдающие инварианты
- Изменить модификаторы доступа свойств на
public get; protected set;
- Создать публичные методы вместо публичных property setters
- Создать спецификации
- Удалить foreign key
<FK>Id
properties
- Блеск и нищета модели предметной области
- Entity Base Class
- Value Object: a better implementation
- In Defense of Lazy Loading
- Защита от NRE
- Соблюдение инвариантов
- Лучшая читаемость кода
- Следование принципам ООП
- Возможное дублирование валидации в конструкторах и в валидаторах
- Ухудшение производительности
- Сложность моделирования
- Куда мне класть этот метод?
- Дублирование кода в валидаторах/контроллерах
- Повторные загрузки сущностей
- Создание обобщенных валидаторов
- Использовать фабрику контекстов
- Использовать обобщенные валидаторы
- Добавить в параметры методов контроллеров
Func<T, TObjectContext<T>>
- Использовать фабрику для инициализации контекстов операций
- Добавить
DataAnnotation
атрибуты на обязательные поля - Использовать
DataAnnotationValidator
по-умолчанию
- Нет повторных чтений из БД
- Обобщенная валидация
- Не используются Nullable Reference Type
- Объединить паттерн
State
и идеи функционального программирования - Запрограммировать конечный автомат
- Создать класс на каждое состояние заказа, распределеить методы между классами-состояниями
- Использовать контекст для валидации
- Designing with types: Making illegal states unrepresentable
- Шаблон проектирования «состояние» двадцать лет спустя
- Компилируется - значит работает
- Переходы между состояниями и правила - явные
- Смешение ответственности между валидаторами и
State
- The CAP theorem of domain modeling
- Asynchronous Injection
- Не тестировать моки
- Перенести работу с IO в отдельный слой
- Не потерять преимущества обобщенных интерфейсов, но сохранить холистические абстракции
- Использовать
IDomainHandler
в качествеDomain Service
- Использовать
CommandResult
для распределенных операций, которые могут завершиться ошибкой
- Более формальные правила структурирования кода
- Упрощение тестирования
- Фрагментация логики
- Отделение
мух от котлетосновного процесса от побочных эффектов - Коммуникация между модулями
- Упрощение перехода к шине данных и микросервисам
- Аудит лог
- Добавить обработку событий в пайплайн
- Запрограммировать диспетчер
- Добавить шаг с обработкой событий в пайплайн
- Domain events: design and implementation
- Better domain event pattern
- Multiple dispatch в C#
- Event storming
- Слабая связанность
- Неявность
- Управление транзакциями
- События в обработчиках событий
- Синхронные и асинхронные обработчики
- Производительность: импорт, экспорт, массовое обновление, денормализация
- Реализовать массовую операцию с помощью
Bulk Extensions
- Добавить публикацию события в модуле
- В другом модуле подписаться на событие
- Выполнить массовую операцию в обработчике события
- Не нужно писать хранимые процедуры
- LINQ нарушает LSP