- Создание настраиваемой сущности
- Обзор для создания сущности
- Создание сущности
- Сущности в DDD-стиле с Entity Framework Core
- Сущности в DDD-стиле
- Сравнение создание сущности
- a. Стандартный подход
- b. В DDD-стиле
- Сравнение изменения свойств
- 1. Изменение даты публикации
- a. Сущность с публичными свойствами
- b. Сущность в DDD-стиле
- 2. Управление скидкой для книги
- 3. Работа с агрегатом – свойство-коллекция Reviews
- Заключение
- Плюсы и минусы DDD-сущностей при работе с EF Core
- Плюсы
- Минусы
- Другие аспекты DDD, не раскрытые в этой статье
Создание настраиваемой сущности
Эта тема относится к Dynamics 365 Customer Engagement (on-premises). Версию Power Apps этой темы см. в следующем разделе: Создание настраиваемой сущности
Прежде чем создать пользовательскую сущность, оцените, соответствует ли существующая сущность вашим требованиям. Дополнительные сведения: Создание новых или использование существующих метаданных
Часть имени любой создаваемой настраиваемой сущности — это префикс настройки. Это настраивается с использованием издателя решений для решения, в котором выполняется работа. Если важен префикс настройки, убедитесь, что вы работаете с неуправляемым решением или решением по умолчанию, префикс настройки в котором — тот, который нужен для данной сущности. Сведения о том, как изменить префикс настройки, см. в разделе Изменение префикса издателя решения.
Обзор для создания сущности
Минимальные обязательные поля для создания пользовательской сущности.
Поле | Описание |
---|---|
Отображаемое имя | Это имя сущности в единственном числе, которое будет отображаться в приложении. |
Имя во множественном числе | Это имя сущности во множественном числе, которое будет отображаться в приложении. |
Название | Это поле предварительно заполняется в зависимости от введенного отображаемого имени. Сюда относится префикс настройки издателя решения. |
Тип собственности | Можно выбрать принадлежность пользователю, группе или организации. Дополнительные сведения: Владение сущностью |
- При использовании Safari в качестве браузера может появиться сообщение об истечении времени ожидания при попытке сохранить или опубликовать новую настраиваемую сущность. В этом случае рекомендуем использовать другой браузер для создания сущностей.
- Чтобы имя сущности работало во встроенном поиске по базе знаний Dynamics 365 for Customer Service, максимальная длина имени сущности, включая префикс издателя, не может превышать 24 знаков.
Для создания сущности действия выберите Определить как сущность действия, прежде чем сущность будет сохранена. Дополнительные сведения: Сущности действий
В разделе Области, в которых отображается данная сущность выберите, в каких разделах области навигации должна быть доступна эта сущность. Это не является обязательным, но если нужно, чтобы люди могли легко найти сущность, выберите один из этих вариантов. Внесение изменений здесь обновляет данные, определяющие область навигации. Невозможно изменить параметры для системных сущностей. Однако можно редактировать эти данные, чтобы изменить место отображения каждой сущности и способ ее отображения. Для этого нужно отредактировать карту сайта.
Существует несколько параметров, задаваемых по умолчанию. Если вы не уверены, что они нужны для пользовательской сущности, отключите их перед сохранением. Всегда можно выбрать включить их позднее, но ряд параметров невозможно отключить после включения. Примечания, Действия и Подключения по умолчанию включены и не могут быть отключены позднее. Дополнительные сведения о доступных параметрах см. в разделе Изменение сущности.
Каждая настраиваемая сущность имеет основное поле. Оно определено на вкладке Основное поле. Это поле используется, если записи для сущности отображаются в списке. Основное поле — это, как правило, ссылка, открывающая запись. Это поле должно быть полем Строка текста с форматом Текст. При создании сущности единственное значение, которое нельзя изменить впоследствии, это Имя. По умолчанию Отображаемое имя — это «Имя», а Имя — это префикс настройки издателя решений, нижнее подчеркивание и «Имя». Если такое название не подходит, внесите изменения до создания сущности. После сохранения сущности невозможно редактировать основные значения полей с вкладки «Основное поле» для этой сущности. Необходимо найти это поле в полях сущности. Вы сможете редактировать его, как и любые другие поля с одной строкой текста.
Пользователи с ролями «Системные администратор» или «Настройщик системы» могут просматривать все пользовательские сущности. Это позволяет тестировать пользовательские сущности до отображения их пользователям. Прежде чем пользователи с другими ролями безопасности смогут просмотреть эти сущности. необходимо отредактировать роли безопасности и предоставить доступ другим пользователям, чтобы они могли его увидеть. При создании пользовательской сущности она будет включена на вкладке «Настраиваемые сущности» для каждой роли безопасности. Необходимо предоставить хотя бы доступ на чтение уровня пользователя к пользовательской сущности, прежде чем ее можно будет увидеть в приложениях.
При создании новой сущности для не создается несколько записей метаданных и вспомогательных систем. Пользователь продолжает редактировать сущность, работая с ними.
Создание сущности
Например, создатель или настройщик приложения может создавать и изменять сущности с помощью обозревателя решений.
- Выполните вход в экземпляр Dynamics 365 Customer Engagement (on-premises).
- Запустите обозреватель решений. Параметры >Настройки >Настройка системы.
- В списке Тип компонента выберите Создать, затем выберите Сущность.
- Введите следующие необходимые сведения. Дополнительные сведения о доступных необязательных свойствах см. в разделах Параметры сущностей, которые можно только включить и Включение или отключение параметров сущности.
- Отображаемое имя. Определяет подпись, отображаемую в приложениях для ссылок и записей созданных сущностей, основанных на этой сущности, например Мини-приложение.
- Имя во множественном числе. Эта подпись отображается в приложениях для ссылок на записи сущности, которые могут содержать несколько записей, например Мини-приложения.
- Имя. Это поле заранее заполняется на основе введенного значения Отображаемое имя и включает префикс настройки издателя решения, например new_widget. Значение Отображаемое имя можно изменить позже, но значение Имя невозможно изменить после сохранения поля. Дополнительные сведения: Изменение префикса издателя решения для издателя по умолчанию
Источник
Сущности в DDD-стиле с Entity Framework Core
Эта статья о том, как применить принципы Domain-Driven Design (DDD) к классам, отображаемым Entity Framework Core (EF Core) на базу данных, и почему это может быть полезно.
В DDD-подходе есть множество преимуществ, но главное – DDD переносит код операций создания / изменения внутрь класса сущности. Это значительно понижает шансы неверного понимания / интерпретации разработчиком правил создания, инициализации и использования экземпляров классов.
- В книге Эрика Эванса и его выступлениях не так много информации на этот счет:
- Предоставьте клиенту простую модель для получения постоянных объектов (классов) и управления их жизненным циклом.
- Ваши классы сущностей должны явно сообщать о том могут ли они быть изменены, как именно и по каким правилам.
- В DDD существует понятие агрегат. Агрегат – это дерево связанных сущностей. По правилам DDD работа с агрегатами должна осуществляться через «корень агрегации» (корневую сущность дерева).
Эрик в своих выступлениях упоминает репозитории. Я не рекомендую реализовывать репозиторий вместе с EF Core, потому что EF уже реализует паттерны «репозиторий» и «единица работы» сам по себе. Подробнее об этом я рассказываю в отдельно статье «стоит ли использовать репозиторий вместе с EF Core».
Сущности в DDD-стиле
Я начну с того, что покажу код сущностей в DDD-стиле и затем сравню их с тем как обычно создают сущности с EF Core (прим. переводчика. автор называет словом «обычно» анемичную модель»). Для примера я буду использовать базу данных интернет-магазина по продаже книг (очень упрощенную версия Амазона». Структура БД показана на изображении ниже.
Первые четыре таблицы представляют все что касается книг: сами книги, их авторы, обзоры. Две таблицы внизу используются в коде бизнес-логике. Эта тема подробно раскрыта в отдельной статье.
Весь код этой статьи выложен в репозиторий GenericBizRunner на GitHub. Кроме кода библиотеки GenericBizRunner там есть еще пример ASP.NET Core приложения, использующего GenericBizRunner для работы с бизнес-логикой. Больше об этом написано в статье «библиотека для работы с бизнес-логикой и Entity Framework Core».
А вот и код сущностей, соответствующий структуре БД.
На что обратить внимание:
- Строка 5: set-доступ ко всем свойствам сущностей объявлен приватным. Это значит, что данные могут быть изменены либо с помощью конструктора, либо с помощью публичных методов, описанных ниже в этой статье.
- Строки 9 и 10. Связанные коллекции (те самые агрегаты из DDD) предоставляют публичный доступ к IEnumerable , а не ICollection . Это значит, что вы не сможете добавить или удалить элементы из коллекции напрямую. Вам придется использовать специализированные методы из класса Book.
- Строка 13. EF Core требует беспараметрический конструктор, но он может иметь приватный доступ. Это значит, что другой прикладной код, не сможет обойти инициализацию и создать экземпляры классов с помощью беспараметрического конструктора (прим. переводчика. Если вы конечно не создаете сущности исключительно с помощью reflection)
- Строки 16-20: единственный способ, с помощью которого вы сможете создать экземпляр класса Book – использовать публичный конструктор. Этот конструктор содержит всю необходимую информацию для инициализации объекта. Таким образом, объект гарантированно будет находиться в разрешенном (valid) состоянии.
- Строки 23-25: На этих строчках располагаются методы, позволяющие изменить состояние книги.
- Строки 28-29: Эти методы позволяют изменить связанные сущности (агрегаты)
Методы на строках 23-39 я буду далее называть «методы, предоставляющие доступ». Эти методы – единственный способ изменить свойства и связи внутри сущности. В сухом остатке класс Book «закрыт». Он создается через специальный конструктор и может быть изменен только частично через специальные методы с подходящими названиями. Такой подход создает резкий контраст со стандартным подходом к созданию / изменению сущностей в EF Core, в котором все сущности содержат пустой конструктор по-умолчанию и все свойства объявлены публичными. Следующий вопрос, почему первый подход лучше?
Сравнение создание сущности
Сравним код получения данных о нескольких книгах из json и создания на их основе экземпляров классов Book.
a. Стандартный подход
b. В DDD-стиле
Код конструктора класса Book
На что обратить внимание:
- Строки 1-2: конструктор заставляет вас передать все необходимые для правильной инициализации данные.
- Строки 5, 6 и 17-9: код содержит несколько проверок бизнес-правил. В данном конкретном случае нарушение правил рассматривается как ошибка в коде, поэтому в случае нарушения будет выброшено исключение. Если бы пользователь мог исправить эти ошибки, возможно, я бы использовал статическую фабрику, возвращающую Status (прим. переводчика. Я бы использовал Option или Result , как более широко-употребимое название). Status – это тип, возвращающий список ошибок.
- Строки 21-23: Связь BookAuthor создается в конструкторе. Конструктор BookAuthor может быть объявлен с уровнем доступа internal. Таким образом мы сможем предотвратить создание связей вне DAL.
Как вы могли заметить, объем кода для создания сущности в обоих случаях примерно одинаков. Так почему же DDD-стиль лучше? Стиль DDD лучше тем, что:
- Контролирует доступ. Случайное изменение свойства исключено. Любое изменение происходит через конструктор или публичный метод с соответствующим названием. Совершенно очевидно, что происходит.
- Соответствует DRY (don’t repeat yourself). Вам может потребоваться создавать экземпляры Book в нескольких местах. Код присвоения находится в конструкторе и вам не придется повторять его в нескольких местах.
- Скрывает сложность. В классе Book есть два свойства: ActualPrice и OrgPrice. Оба этих значения должны быть равны при создании новой книги. В стандартном подходе каждый разработчик должен знать об этом. В DDD-подходе достаточно, чтобы об этом знал разработчик класса Book. Остальные узнают об этом правиле, потому что оно явным образом записано в конструкторе.
- Скрывает создание агрегата. В стандартном подходе разработчик должен вручную создать экземпляр BookAuthor. В DDD-стиле эта сложность инкапсулирована для вызывающего кода.
- Позволяет свойствам иметь приватный доступ на запись
- Одна из причин использования DDD – «запереть» (lock down) сущности, т.е. не давать возможности изменять свойства напрямую. Давайте сравним операцию изменения с использованием DDD и без.
Сравнение изменения свойств
Прим. переводчика. Оригинальная фраза труднопереводима на русский язык. В данном случае design decisions – это принятые решения о том, как ПО должно работать. Имеется в виду, что решения обсуждались и подтверждены. Код с конструкторами, корректно инициализирующими сущности и методами с корректными названиями, отражающими смысл операций явным образом сообщает разработчику о том, что присвоения определенных значений сделаны с умыслом, а не по ошибке и не являются прихотью другого разработчика или деталями реализации.
Я понимаю эту фразу следующим образом.
- Сделайте очевидным, как изменять данные внутри сущности и какие данные должны изменяться вместе.
- Сделайте очевидным когда вы не должны изменять определенные данные в сущности.
Давайте сравним два подхода. Первый пример – простой, а второй – посложнее.
1. Изменение даты публикации
Допустим, мы хотим сначала поработать с черновиком книги и лишь затем опубликовать. В момент создания черновика устанавливается ориентировочная дата публикации, которая весьма вероятно, будет изменена в процессе редактирования. Для хранения даты публикации будем использовать свойство PublishedOn.
a. Сущность с публичными свойствами
b. Сущность в DDD-стиле
В DDD-стиле setter свойства объявлен приватным, поэтому мы будем использовать специализированный метод доступа.
Эти два случая почти не отличаются. DDD-вариант даже немного длиннее. Но разница все-же есть. В DDD-стиле вы точно знаете, что дата публикации может быть изменена, потому что существует метод с очевидным названием. Вы также знаете, что вы не можете изменить издателя, потому что для свойства Publisher нет соответствующего метода для изменения. Эта информация будет полезна любому программисту, работающего с классом книги.
2. Управление скидкой для книги
Другое требование — мы должны иметь возможность управлять скидками. Скидка состоит из новой цены и комментария, например «50% до конца этой недели!»
Реализация этого правила простая, но не слишком очевидная.
- Свойство OrgPrice — это цена без учета скидки.
- ActualPrice — текущая цена, по которой продается книга. Если скидка действует, то текущая цена будет отличаться от OrgPrice на размер скидки. Если нет, то значение свойств будет равно.
- Свойство PromotionText должно содержать текст скидки, если скидка применена или null, если в данный момент скидка не применяется.
Правила довольно очевидны для того, кто их реализовывал. Однако для другого разработчика, скажем, разрабатывающего UI для добавления скидки. Добавление методов AddPromotion и RemovePromotion в класс сущности скрывает детали реализации. Теперь у другого разработчика есть публичные методы с соответствующими названиями. Семантика использования методов — очевидна.
Взглянем на реализацию методов AddPromotion и RemovePromotion.
На что обратить внимание:
- Строки 4 -10: добавление комментария PromotionalText обязательно. Метод проверяет, что текст не пустой. Т.к. Эту ошибку пользователь может исправить метод возвращает список ошибок для исправления.
- Строки 12, 13: метод устанавливает значения свойств в соответствие с реализацией, которую выбрал разработчик. Пользователю метода AddPromotion не обязательно знать их. Чтобы добавить скидку достаточно написать просто:
Метод RemovePromotion гораздо проще: он не предполагает обработки ошибок. Поэтому возвращаемое значение просто void.
Эти два примера сильно отличаются друг от друга. В первом примере изменение свойства PublishOn на столько простое, что стандартная реализация вполне подходит. Во втором примере детали реализации не очевидны для того, кто не работал с классом Book. Во втором случае DDD-стиль со специализированными методами доступа скрывает детали реализации и делает жизнь других разработчиков проще. Также, во втором примере код содержит бизнес-логику. Пока объём логики небольшой мы можем хранить ее прямо в методах доступа и возвращать список ошибок, если метод используется не правильно.
3. Работа с агрегатом – свойство-коллекция Reviews
DDD предлагает работать с агрегатом только через корень. В нашем случае свойство Reviews создает проблемы. Даже если setter будет объявлен приватным, разработчик все-равно может добавить или удалить объекты с помощью методов add и remove или даже вызвать метод clear, чтобы очистить коллекцию целиком. Здесь нам поможет новая функция EF Core — backing fields.
Backing field позволяет разработчику инкапсулировать настоящую коллекцию и предоставить публичный доступ к интерфейсной ссылке IEnumerable . Интерфейс IEnumerable не предоставляет методов add, remove или clear. В коде ниже пример использования backing fields.
Чтобы это сработало нужно рассказать EF Core что при чтении из бд нужно записывать в приватное поле, а не публичное свойство. Код конфигурации показан ниже.
Для работы с обзорами я добавил два метода: AddReview и RemoveReview классу книги. Метод AddReview более интересен. Вот его код:
На что обратить внимание:
- Строки 4-7: я намеренно не инициализирую поле _reviews в приватном беспараметрическом конструкторе, который EF Core использует, когда загружает сущности из БД. Это позволяет моему коду определить была ли загружена коллекция с помощью метода .Include(p => p.Reviews). В публичном конструкторе я инициализирую поле, так что NRE при работе с созданной сущностью не произойдет.
- Строки 8-12: Если коллекция Reviews не была загружена код должен использовать DbContext для инициализации.
- Строки 13-16: Если книга была успешно создана и содержит ID, то я использую другую технику добавления обзора: просто устанавливаю foreign key в экземпляре класса Review и записываю в БД. Более подробно об этом написано в секции 3.4.5 моей книги.
- Строка 19: Если мы оказались здесь, то есть какая-то проблема с логикой кода. Поэтому я выбрасываю исключение.
Я спроектировал все мои методы доступа для обратил случая, когда загружена только корневая сущность. Как обновить агрегат остается на усмотрение методов. Возможно потребуется загрузка дополнительных сущностей.
Заключение
Чтобы создавать сущности в DDD-стиле с EF Core необходимо придерживаться следующих правил:
- Создавайте публичные конструкторы для создания корректно инициализированных экземпляров классов. Если в процессе создания могут произойти ошибки, которые пользователь может исправить, создавайте объект не с помощью публичного конструктора, а с помощью фабричного метода, возвращающего Status , где T – тип создаваемой сущности
- Все свойства setter’ы свойств приватные. Т.е. все свойства – read-only за пределами класса.
- Для навигационных свойств коллекций объявляйте backing fields, а тип публичного свойства объявляйте IEnumerable . Это не позволит другим разработчикам неконтролируемо изменять коллекции
- Вместо публичных setter’ов создавайте публичные методы для всех разрешенных операций изменения объекта. Эти методы должны возвращать void, если операция не может завершиться ошибкой, которую пользователь может исправить или Status — если может.
- Рамки ответственности сущности имеет значение. Я думаю, что лучше всего ограничить сущности изменением самого класса и других классов внутри агрегата, но не за пределами. Правила валидации должны быть ограничены проверками правильности создания и изменения состояния сущностей. Т.е. я не проверяю такие бизнес-правила, как остатки на складе. Для этого существует специальный код бизнес-логики.
- Методы, изменяющие состояния должны предполагать, что загружен только корень агрегации. Если методу требуется загрузка других данных, он должен позаботиться об этом самостоятельно.
- Методы, изменяющие состояния должны предполагать, что загружен только корень агрегации. Если методу требуется загрузка других данных, он должен позаботиться об этом самостоятельно. Такой подход упрощает использование сущностей другими разработчиками.
Плюсы и минусы DDD-сущностей при работе с EF Core
Мне нравится критический подход к любому паттерну или архитектуре. Вот, что я думаю по поводу использования DDD-сущностей.
Плюсы
Минусы
- Мой подход содержит зависимости от реализации EF Core.
- Некоторые люди даже называют это анти-паттерном. Проблема в том, что теперь сущности предметной модели зависят от кода доступа к базе данных. В терминах DDD – это плохо. Я осознал, что если бы я не сделал этого, то мне бы пришлось полагаться на то, что вызывающий код знает о том, что должно быть загружено. Такой подход ломает принцип разделения ответственности (separation of concerns).
- DDD заставляет писать больше кода.
Действительно ли это стоит того в простых случаях, вроде обновления даты публикации книги?
Как вы могли заметить мне нравится DDD-подход. Однако, мне потребовалось какое-то время, чтобы правильно его структурировать, но на данный момент подход уже устаканился и я применяю его в проектах, над которыми работаю. Я уже успел попробовать этот стиль а небольших проектах и доволен, но все плюсы и минусы еще предстоит узнать, когда я применю его в больших проектах.
Мое решение разрешить использовать код, специфичный для EFCore в аргументах методов сущностей предметной модели было не простым. Я пытался не допустить этого, но в итоге пришел к тому, что вызывающему коду приходилось загружать множество навигационных свойств. И если этого не сделать, то изменение просто не будет применено без каких-либо ошибок (особенно в отношениях один-к-одному). Это было для меня не приемлемо, поэтому я разрешил использование EF Core внутри некоторых методов (но не конструкторов).
Другая плохая сторона в том, что DDD заставляет писать значительно больше кода для CRUD-операций. Я до сих пор не уверен, следует ли продолжать есть кактус и писать отдельные методы для всех свойств или же в некоторых случаях стоит отойти от столь радикального пуританства. Я знаю, что есть просто вагон и маленькая тележка скучного CRUD’а, который проще написать напрямую. Только работа над реальными проектам покажет, что лучше.
Другие аспекты DDD, не раскрытые в этой статье
Статья итак получилось чересчур длинной, поэтому я собираюсь закончить здесь. Но, это значит, что есть еще много нераскрытого материла. О чем-то я уже писал, о чем-то я буду писать в ближайшем будущем. Вот, что осталось за бортом:
Источник