DTO, маппинг и контракты API

↑ 3.3 Web API: контроллеры, Minimal API, валидация · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Проверяют, отделяете ли вы модель хранения от внешнего контракта, и как относитесь к AutoMapper.

Объяснение

DTO (Data Transfer Object) — форма данных на границе API. Контракт отделяется от сущностей БД и доменных моделей, чтобы менять их независимо и не «светить» лишнего (пароль-хэши, внутренние ID).

public record OrderDto(Guid Id, string Customer, decimal Total, IReadOnlyList<OrderItemDto> Items);
public record CreateOrderRequest(string Customer, IReadOnlyList<OrderItemRequest> Items);
Способ маппингаПлюсыМинусы
Ручной (ToDto())явный, быстрый, легко отлаживатьмного кода
Mapperly / source generatorsбез рефлексии, проверка на этапе компиляциигенерируемый код
AutoMapperменьше кодарефлексия, скрытая логика, ошибки в рантайме, лицензия
Проекция в запросе (Select)грузит только нужные поляпривязка к EF
var dtos = await db.Orders
    .Where(o => o.CustomerId == id)
    .Select(o => new OrderDto(o.Id, o.Customer.Name, o.Total, ...))
    .ToListAsync(ct);

Контракты API

  • Разделяйте запросы и ответы; не переиспользуйте одну модель для create/update/response.
  • Добавление полей — совместимо, удаление и смена типа — ломающее изменение.
  • Публикуйте схему (OpenAPI) и версионируйте.
  • Используйте record, init, nullable-аннотации для ясности контракта.

Нюансы и подводные камни

  • Возврат EF-сущностей ведёт к циклам сериализации, ленивым запросам и утечке данных.
  • Mass assignment (over-posting): не биндите тело прямо в сущность.
  • Не смешивайте бизнес-логику в мапперах.
  • Даты передавайте в ISO 8601 UTC, деньги — как decimal/строка, а не double.

Практика

  1. Замените возврат сущностей на DTO и проекции Select.
  2. Реализуйте маппинг через Mapperly.
  3. Проверьте over-posting: добавьте поле IsAdmin и попробуйте его отправить.

Вопросы с ответами

Связанные темы