Больше запросов ServiceStack Совет DTO

Это продолжение:

ServiceStack Request DTO design

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

[Route("/bookinglimits/","POST")]
public class CreateBookingLimit : IReturn<BookingLimit>
{      
     BookingLimit newBookingLimit
}

-OR- Будет ли это лучший дизайн?

[Route("/bookinglimits/","POST")]
public class CreateBookingLimit : IReturn<BookingLimit>
{      
  public int ShiftId { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
  public int Limit { get; set; }    }
}

Кроме того, если мы хотим добавить редактирование - должны ли мы вставлять и редактировать совместно использовать одни и те же модели и добавлять идентификатор?

[Route("/bookinglimits/","POST")]
[Route("/bookinglimits/{Id}/","PUT")]
public class CreateBookingLimit : IReturn<BookingLimit>
{      
  public int Id { get; set; }
  public int ShiftId { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
  public int Limit { get; set; }    }
}

Я пытаюсь оборачивать голову, когда имеет смысл повторно использовать POCOs и когда имеет смысл разделить намерения.

Ответ 1

Дизайн API на основе сообщений

При проектировании идеального API-интерфейса, основанного на сообщениях, есть несколько вещей, в которых ваши службы эффективно работают с двумя мастерами: API-интерфейс для собственного клиента и API REST. "Родные клиенты" просто отправляют и получают сообщения в их первоначальной форме, поэтому получают естественный API для бесплатного моделирования с использованием CTO Request and Response DTO для сбора какой информации требуется, чтобы Служба выполнила свою Операцию и что она должна вернуть.

Проецирование сообщений в идеальный HTTP API

После разработки вашего API на основе сообщений вам будет необходимо сосредоточиться на том, как лучше проецировать сообщения в REST API, аннотируя запросы DTO с атрибутами [Route] для определения пользовательских конечных точек для ваши услуги.

В этом предыдущем ответе Проектирование службы REST-сервиса с ServiceStack приведены примеры, по которым маршрутизируются разные запросы DTO, в общем, вы захотите разработать свои API-интерфейсы вокруг Ресурсы, где каждая операция "действует на ресурс", что упростит определение ваших пользовательских маршрутов. Идеальный HTTP API для создания и обновления лимита бронирования будет выглядеть так:

POST /bookinglimits       (Create Booking Limit)
PUT  /bookinglimits/{id}  (Update Booking Limit)

Общие рекомендации по хорошему дизайну API

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

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

Внедрение ServiceStack

Чтобы реализовать это в ServiceStack, я бы не использовал один и тот же запрос DTO для поддержки нескольких глаголов. Так как запрос DTO называется Create*, который передает, что пользователи должны отправлять этот запрос DTO для резервирования, который обычно выполняется с использованием запроса POST, например:

[Route("/bookinglimits", "POST")]
public class CreateBookingLimit : IReturn<CreateBookingLimitResponse>, IPost
{      
  public int ShiftId { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
  public int Limit { get; set; } 
}

public class CreateBookingLimitResponse
{
    public BookingLimit Result { get; set; }
    public ResponseStatus ResponseStatus { get; set; }
}

IPut, IPost Маркеры интерфейса Verb, которые позволяют пользователю и клиенту службы знать, какое глагол это сообщение должно быть отправленный с помощью которого можно отправить все сообщения в один метод Service Gateway.

Если ваша Служба также поддерживает обновление лимита бронирования, я создам для него отдельную услугу, которая будет выглядеть так:

[Route("/bookinglimits/{Id}", "PUT")]
public class UpdateBookingLimit : IReturn<UpdateBookingLimitResponse>, IPut
{      
  public int Id { get; set; }
  public int ShiftId { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
  public int Limit { get; set; } 
}

public class UpdateBookingLimitResponse
{
    public BookingLimit Result { get; set; }
    public ResponseStatus ResponseStatus { get; set; }
}

Используя отдельные операции, вы можете гарантировать, что Request DTOs содержит только свойства, относящиеся к этой операции, что уменьшает путаницу для пользователей API.

Если это имеет смысл для вашего Сервиса, например. схемы для обеих операций остаются одинаковыми. Я объединю обе операции Create/Update в одну операцию. Когда вы это делаете, вы должны использовать согласованный глагол, который указывает, когда операция выполняет обе, например. Store* или CreateOrUpdate*:

[Route("/bookinglimits", "POST")]
public class StoreBookingLimit : IReturn<StoreBookingLimitResponse>, IPost
{      
  public int Id { get; set; }
  public int ShiftId { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
  public int Limit { get; set; } 
}

public class StoreBookingLimitResponse
{
    public BookingLimit Result { get; set; }
    public ResponseStatus ResponseStatus { get; set; }
}

В большинстве случаев, когда Сервер генерирует Идентификатор ресурса, вы должны использовать POST, в редком случае, когда клиент указывает идентификатор, например. Slug или Guid вы можете использовать PUT, который грубо переводит на "PUT этот ресурс в этом месте", который возможен, когда клиент знает URL-адрес ресурса.

Примеры API на основе сообщений

В большинстве случаев, какие сообщения должны содержать, будут очевидны на основе требований к Сервису и становятся интуитивно понятными и естественными для создания с течением времени. В примерах всеобъемлющего API на основе сообщений вы можете посмотреть веб-службы AWS, которые эффективно обслуживали свои веб-службы за дизайном на основе сообщений, который использует клиентов службы для отправки сообщений для доступа ко всем своим API-интерфейсам, например. AWS Справочник по API DynamoDBперечисляет все доступные Действия, а также другие типы DTO, возвращаемые Сервисом, например, здесь находятся API-интерфейсы DynamoDB, связанные с созданием/изменением и запросом элементов:

Действия

  • BatchGetItem
  • BatchWriteItem
  • DeleteItem
  • GetItem
  • PutItem
  • Запрос
  • Сканирование
  • UpdateItem

Типы данных

  • AttributeDefinition
  • AttributeValue
  • AttributeValueUpdate
  • Состояние
  • ...

В действиях ServiceStack называются Операции и что вы будете использовать Request DTO для определения, в то время как типы данных AWS называются только DTO, которые я сохраняю в пространстве имен Types, чтобы отличать от Operations.

DynamoDb.ServiceModel (project)

/GetItem
/PutItem
/UpdateItem
/DeleteItem
/Query
/Scan

/Types 
  /AttributeDefinition
  /AttributeValue
  /AttributeValueUpdate

Обычно вам не нужны дополнительные явные службы для пакетных запросов, так как вы можете получить это бесплатно, используя ServiceStack Auto Batch Requests. ServiceStack также включает в себя ряд других преимуществ, когда он способен генерировать более богатые DTO, содержащие пользовательские атрибуты и интерфейсы в исходных DTO, чтобы включить более насыщенные и сжатые конечные точки, end typed API, требующий меньшего количества кода и сгенерированного кода, который позволяет использовать один и тот же Клиент общих служб для вызова любой службы ServiceStack, предлагающей как Sync, так и идиоматические API Async. Дополнительные метаданные также обеспечивают бесшовные функциональные возможности более высокого уровня, такие как Encrypted Messaging, Cache Aware Clients, Несколько форматов, Service Gateway, Маркеры интерфейса HTTP-вершин и т.д.

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