Несколько Dtos для одной и той же сущности - PullRequest
0 голосов
/ 28 мая 2018

Рекомендуется ли использовать несколько DTO для одной и той же сущности в разных конечных точках API.Например: у меня есть конечная точка API, которая принимает следующее Dto:

public class AddressDto
{
    public string City { get; set; }
    public string Country { get; set; }
    public string Contact { get; set; }
    public string Street1 { get; set; }
    public string Street2 { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

И теперь есть второй Api, который принимает тот же dto, но в этом вызове API я использую только Streer1, Street2, Contact, все остальныеигнорируется.

Должен ли я сделать еще один DTO за секунду api endpoint, например:

public class AddressDtoForSecondAPI
{
    public string Contact { get; set; }
    public string Street1 { get; set; }
    public string Street2 { get; set; }
}

1 Ответ

0 голосов
/ 28 мая 2018

Короче говоря, да, это приемлемо.


Однако, как вы можете видеть в комментариях и других ответах, не все согласны здесь.Итак, позвольте мне объяснить мой ответ.

Аргумент 1 - вводит в заблуждение потребителя

И теперь есть второй Api, который принимает тот же dto, но в этом вызове APIЯ использую только Streer1, Street2, Contact, все остальные игнорируются.

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

Это фактически то же самое, что:

public int AddNumbersTogether(int a, int b, int c, int d)
{
    return a + c + d; //we ignore b
}

Нет никаких оснований для существования b.Любой, кто использует этот метод, будет чесать голову, когда AddNumbersTogether(1,2,3,4) возвращает значение 8.Синтаксис противоречит поведению.

Да, параметр неиспользуемого метода легче опустить, чем разрабатывать второй DTO.Но вы должны быть последовательны и придерживаться того же принципа: не вводить в заблуждение потребителя .

Аргумент 2 - DTO не является сущностью

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

Именно поэтому вы используете DTO, а не класс сущностей для начала.!Вы обеспечиваете логическое разделение между выполнением действия и хранением данных этого действия.

Потребителю все равно, где хранятся данные.Независимо от того, сохраняете ли вы улицу в той же таблице, что и адрес, или в другой таблице (или базе данных) в целом, не имеет значения в области действия потребителя, вызывающего метод API.

Аргумент 3 - Противодействие S.Akbari

А как насчет наследования и / или принципа разделения интерфейса в SOLID ?- S.Akbari

Это недопустимые аргументы для данного конкретного случая.

Наследование является ошибочным подходом.Да, вы можете технически сделать что-то вроде AddressDto : AddressDtoForSecondAPI в опубликованном примере кода, но это огромный запах кода.
Что происходит, когда нужен третий DTO, например тот, где толькопочтовые индексы и названия городов используются?Вы не можете иметь AddressDto наследовать от нескольких источников, и нет логического перекрытия между AddressDtoForSecondAPI и вновь созданными AddressDtoForThirdAPI.

Интерфейсы не являются решением здесь.Да, вы могли бы технически создать интерфейс IAddressDtoForSecondAPI и IAddressDtoForThirdAPI с соответствующими полями, а затем сделать что-то вроде AddressDto : IAddressDtoForSecondAPI, IAddressDtoForThirdAPI.Однако это тот же самый массивный запах кода снова.

Что произойдет, если у второго и третьего варианта будет несколько общих свойств и несколько отдельных?Если вы применяете сегрегацию интерфейса, то перекрывающиеся свойства должны быть абстрагированы в интерфейсе сами по себе.
Если затем появляется четвертый вариант, который имеет некоторые общие свойства со вторым вариантом, некоторые с третьим вариантом, некоторые си во втором, и в третьем варианте, и в некоторых отдельных свойствах, вам потребуется создать еще больше интерфейсов!

Учитывая достаточно вариантов одного и того же объекта и многократно применяя принцип разделения интерфейса;вы получите интерфейс для каждого свойства объекта;что требует смешного количества накапливания.В итоге вы получите что-то вроде:

public class AddressDto : IAddressCity, IAddressCountry, IAddressContact, IAddressStreet1, IAddressStreet2, IAddressState, IAddressZip
{
    public string City { get; set; }
    public string Country { get; set; }
    public string Contact { get; set; }
    public string Street1 { get; set; }
    public string Street2 { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

Представьте, что вам нужно сделать это для всех классов;поскольку тот же принцип применим к каждому DTO, используемому API.

Аргумент 4 - DRY здесь не применяется

Я понимаю, почему вы 'опасаются создания двух классов.Скорее всего, у вас в голове поднимается флаг ошибки СУХОЙ / ВЛАЖНОЙ.

Избегать WET - хороший рефлекс;но ты не всегда можешь это слушать.Потому что, если вы действительно избегаете дублирования, вам также не следует создавать отдельные сущности и классы DTO, поскольку они обычно являются копиями / вставками друг друга.

DRY не является абсолютным,Принимая пример сущности / DTO, здесь есть баланс соображений:

  • Хотите ли вы избежать повторения любой ценой?(= СУХОЙ)
  • Хотите отделить свой DAL от логики API?(= разделение интересов)

В этом случае последний обычно выигрывает.

В вашем случае применяется тот же аргумент.Аргумент против после DRY (это аргументы, которые я только что перечислил) far перевешивает преимущества следования за DRY в этом сценарии.

...