Является ли хорошей практикой использование объектов-сущностей в качестве объектов передачи данных?


29

Мне интересно, потому что, если это так, почему Entity Framework не предлагает логику для создания нового объекта с такими же свойствами для передачи данных между слоями?

Я использую объекты сущностей, которые я генерирую с помощью структуры сущностей.


3
Я думаю, что это хороший вопрос, но я не могу точно сказать, потому что это так трудно читать, я не уверен, как это исправить. Пожалуйста, отредактируйте свой вопрос.
candied_orange

1
@CandiedOrange +1, и это делает его еще более страшным, что это вызвало столько голосов.
guillaume31

Ответы:


23

Это тебе решать.

Большинство людей скажут вам, что это не очень хорошая практика, но в некоторых случаях вы можете сойти с рук.

EF никогда не играл хорошо с DDD по нескольким причинам, но выделяются две: у вас не может быть параметризованных конструкторов на ваших сущностях, и вы не можете инкапсулировать коллекции. DDD полагается на это, поскольку модель предметной области должна включать как данные, так и поведение.

В некотором смысле, EF вынуждает вас иметь анемичную модель предметной области, и в этом случае вы можете использовать сущности в качестве DTO. Вы можете столкнуться с некоторыми проблемами, если используете свойства навигации, но можете сериализовать эти объекты и отправить их по проводам. Это может быть не практично, хотя. Вам придется контролировать сериализацию для каждой сущности, свойства которой вам не нужно отправлять. Более простой способ - просто разработать отдельные классы, специально предназначенные для передачи данных. Библиотеки, такие как AutoMapper , созданы для этой цели.

Например: предположим, у вас есть класс, вызываемый Personсо следующим определением:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; get; }

    // plus a bunch of other properties relevant about a person
}

Предполагая, что вы хотите отобразить список сотрудников где-то, может быть целесообразно отправить только Id, FirstNameи LastName. Но вам придется отправить все остальные не относящиеся к делу свойства. Это не такая уж большая проблема, если вам не важен размер ответа, но общая идея - отправлять только соответствующие данные. С другой стороны, вы можете разработать API, который возвращает список людей, и в этом случае может потребоваться отправка всех свойств, что имеет смысл сериализовать и отправлять сущности. В этом случае создание класса DTO является дискуссионным. Некоторые люди любят смешивать сущности и DTO, некоторые нет.

Чтобы ответить на ваш обновленный вопрос, EF - ORM. Его работа заключается в сопоставлении записей базы данных с объектами и наоборот. То, что вы делаете с этими объектами до и после прохождения EF, не является частью его забот. И не должно быть.


1
Кратко резюмируя. Каковы различия между DTO и POCO? Разве они не просто хранят данные?
Laiv

1
@ guillaume31 Вы не можете иметь истинную модель предметной области, не принимая во внимание настойчивость. Что хорошего в публичном интерфейсе, если я вынужден использовать отражение (не говоря уже о других видах хаков, при условии, что мое публичное свойство поддерживается частным полем с другим именем) для загрузки моих агрегатов в допустимом состоянии? Если EF не предоставляет мне инфраструктуру для скрытия моих агрегатов с помощью их открытого интерфейса, то мне лучше оставить свою бизнес-логику в другом месте и использовать мой домен анемично, вместо того, чтобы писать дополнительную схему для загрузки их из базы данных.
devnull

1
Таким образом, вы бы предпочли сделать вашу модель предметной области анемичной, чем назвать получателя публичной коллекции таким же образом, как частное поле? (давайте оставим проблему конструктора в стороне, потому что в первую очередь против домена выставляется конструктор, принимающий все поля объекта ...)
guillaume31

1
@ Конрад отсюда : As of EF Core 2.1, only services known by EF Core can be injected. Support for injecting application services is being considered for a future release.мне очень нравится внедрять сервисы приложений в мои сущности.
devnull

1
@ Конрад, я бы прекратил использовать свои доменные объекты в качестве DTO (если бы я использовал их таким образом). DTO полезны независимо от поддержки EF для внедрения сервисов.
devnull

8

Нет.

В идеале DTO будут соответствовать вашим постоянным репозиториям (или таблицам вашей базы данных).

Но ваши бизнес-классы не обязательно совпадают. Вам могут понадобиться дополнительные классы, или отдельные, или присоединенные классы к тому, что у вас есть в базе данных. Если ваше приложение маленькое, вы, возможно, не увидите такого рода проблем, но в средних и больших приложениях это будет происходить часто.

Другое дело, что DTO являются частью того, что имеет дело с постоянством, в то время как ваш бизнес-уровень не должен ничего о них знать.


Объект Command - это DTO, о котором бизнес-уровень должен что-то знать.
devnull

Я думаю, что бизнес-уровень, вероятно, будет вызывать объект команды косвенно, через интерфейс. Я имею в виду DTO как простые объекты, которые переносят данные без какой-либо функциональности. Я не уверен, является ли объект команды DTO ( martinfowler.com/eaaCatalog/dataTransferObject.html ).
Хуан Карлос Эдуардо Ромайна Ac

1
Обычно команды разделяются на фактическую команду и ее обработчик. Команда содержит данные, обработчик - поведение. При таком использовании командная часть содержит только данные, полученные от клиента, и действует как DTO между клиентом и бизнес-уровнем.
devnull

DTO не перетекают из персистентности в домен, они также могут поступать извне, как при передаче данных (например, в REST API). Как указывает devnull, команды и события являются своего рода DTO и передаются на бизнес-уровень. Если обработчики бизнес-уровня или нет, зависит от дизайна.
Laiv

4

Это на самом деле очень плохая идея. У Мартина Фаулера есть статья о местных DTO .

Короче говоря, DTOPattern использовался для передачи данных вне процесса, например, по проводам, а не между слоями внутри одного и того же процесса.


Как и во всех подходах к программированию, не существует одного подходящего размера, в противном случае мы не будем обсуждать и обсуждать различные шаблоны и подходы в каждом приложении, которое мы создаем, мы просто всегда будем использовать одни и те же вещи. DTO - это, по сути, просто POPO, их просто называют, чтобы показать какое-то намерение. Нет ничего с использованием DTO для «передачи данных» между сервисом, контроллером и в поле зрения. В имени или структуре классов нет ничего, что указывало бы или ограничивало, откуда и в какие данные передаются
Джеймс

4

Нет, это плохая практика.

Несколько причин:

  1. Новые поля сущностей будут в Dto по умолчанию . Использование объекта означает, что каждая информация будет доступна для использования по умолчанию. Это может привести вас к раскрытию разумной информации или, по крайней мере, сделает ваш контракт API раздутым, с большим количеством информации, которая не используется для того, кто использует API. Конечно, вы можете игнорировать поля, используя некоторые аннотации (например, @JsonIgnoreиз мира Java), но это приводит к следующей проблеме ...
  2. Много аннотаций / условий / элементов управления на объекте . Вам нужно контролировать то, что вы хотите отправить на Dto, когда имя какого-либо атрибута изменилось (это нарушит контракт), добавить атрибут, который не принадлежит сущности, порядок атрибутов и т. Д. Вскоре вы увидите свой простой сущность с множеством аннотаций, лишними полями и каждый раз будет сложнее понять, что происходит.
  3. Меньше гибкости . С Dto вы можете разбить информацию на большее количество классов, изменить имена атрибутов, добавить новые атрибуты и т. Д. Вы не можете сделать это так легко с такой сущностью, как Dto.
  4. Не оптимизировано . Ваша сущность всегда будет больше, чем простая Dto. Таким образом, у вас всегда будет больше информации, которую нужно игнорировать / сериализовать, и, возможно, будет передаваться больше ненужной информации.
  5. Ленивые проблемы . Используя сущность некоторых фреймворков (например, Hibernate), если вы попытаетесь получить некоторую информацию, которая ранее не загружалась в транзакции базы данных, прокси-сервер ORM не будет присоединен к транзакции, и вы получите какое-то «ленивое исключение» просто чтобы вызвать getметод сущности.

Таким образом, проще и безопаснее использовать какой-либо инструмент картографирования, чтобы помочь вам в этой работе, сопоставляя поля сущностей с Dto.


1

Чтобы завершить то, что сказал @Dherik, основными проблемами использования объектов сущностей в качестве объектов передачи данных являются:

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

  2. Размер данных, которыми вы делитесь между клиентом и сервером: иногда вы не хотите отправлять все содержимое объекта клиенту, чтобы минимизировать размер ответа на запрос. Отделение DTO от объекта является более гибким, чтобы специализировать данные, которые вы хотите отправить в определенных случаях использования.

  3. Видимость и обслуживание. Вы должны управлять своими аннотациями jpa / hibernate на полях вашей сущности и поддерживать аннотации jackson для сериализации в json в одном месте (даже если вы можете отделить их от реализации сущности, поместив их в интерфейс, унаследованный организация). Затем, если вы измените свой контент DTO при добавлении нового поля, другой человек, вероятно, может подумать, что это поле сущности, следовательно, поле соответствующей таблицы в вашей базе данных (даже если вы можете использовать @Transientаннотацию для всех ваших полей DTO для дела ..!).

На мой взгляд, это создает шум, когда вы читаете сущность, но мое мнение, безусловно, субъективно.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.