Должен ли я делиться типами между сервисом веб-API и его клиентом? Каковы другие варианты?

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

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

Говоря о дизайне, наша команда просто очень долго обсуждала , нужно ли делиться типами между сервером и клиентом (например, через пакеты NuGet с версией, которые как сервер, так и клиент будет зависеть от), и в конечном итоге с плюсами и минусами... и мы не можем так или иначе решить.

Типы общего доступа (совместная сборка) между клиентом и сервером

Pros

  • Модель клиента и модель сервера всегда обновляются
  • отсутствие сериализации/десериализации проблем, поскольку одни и те же типы сериализуются/десериализируются
  • нет дублирования

против

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

Отдельные (но структурно эквивалентные) типы для клиента и сервера

Pros

  • клиентская "модель" менее связана с реализацией сервера (просто зеркало вывода Json на сервере, но не имеет отношения "такого же типа" )
  • модель сервера может развиваться без риска взлома любого клиента
  • позволяют улучшить клиентскую модель независимо от модели сервера.
  • клиентская модель является частью клиентского пакета, а не "общим пакетом" для обслуживания между сервером и клиентом.

против

  • дублирование между кодом сервера и кодом клиента
  • подверженная ошибкам задача сохранения синхронизации на стороне сервера и клиентской стороне.

Кажется, предпочтение 50/50 для каждого решения в нашей команде.

Я лично предпочитаю второй вариант, потому что я считаю, что RESt - это развязка, а развязка означает, что клиент не должен заботиться о том, как реализована серверная сторона (какие типы, или же это приложение .NET в любом случае), но хотелось бы, чтобы мы могли избавиться от возможного дублирования, возможно, благодаря генерации кода или что-то в этом роде, но не смогли найти никаких указаний по этому вопросу

Существуют ли другие плюсы и минусы для совместного использования типов между клиентом и сервером?

Если мы не используем их, существуют способы снизить затраты на обслуживание при попытке сохранить модель клиента и модель сервера

Ответ 1

Я бы сказал, что если вы не будете осторожны, второй вариант может оказаться менее RESTful, чем первый. REST меньше относится к де-мутации и больше об управлении и фокусировке связи между клиентом и сервером.

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

В обоих вариантах вы эффективно используете типы между клиентом и сервером. В первом варианте этот общий доступ является явным посредством конкретной реализации, которая может управляться как пакет nuget и версироваться независимо от клиента и сервера.

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

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

Объяснение Типы мультимедиа всегда являются лучшим способом для захвата контрактов и исполнения контрактов между HTTP-клиентами и серверами. Однако, если вы не хотите туда идти, то общая библиотека nuget является лучшим следующим шагом, потому что вы изолируете часть системы, которая используется совместно с реализацией клиента и сервера. Это одна из ключевых целей REST. Тот факт, что вы фактически используете библиотеку реализации этого совместного контракта, влияет только на пользователей, живущих на других платформах, которые не могут использовать эту библиотеку.

Я несколько лет назад придумал термин Web Pack, чтобы описать эту идею использования общего пакета nuget для хранения общей связи. Я написал несколько статей здесь и здесь по этому вопросу.

Ответ 2

У нас была аналогичная дискуссия - с аналогичными плюсами и минусами - и мы взяли гибридный подход. Мы разделили сборку между клиентом и сервером, но только общие интерфейсы. Затем мы создали классы на основе интерфейсов на стороне клиента. Преимущество заключалось в том, что фактические объекты на клиенте и сервере могут меняться независимо.

Ответ 3

Классы модели предметной области в основном определяются для использования сервером. На стороне сервера типы моделей используются методами, определенными внутри контроллеров, для доступа к данным, например, с использованием структуры сущностей.

Но по некоторым причинам вы можете предпочесть передать клиенту другую версию объекта модели. Известный подход заключается в определении классов [DTO] [dto], которые очень похожи, но не совсем совпадают с типами моделей.

В каждом методе в контроллере, когда вы выбираете данные из базы данных, вам нужно отобразить извлеченные данные из формата их модели в сопоставимый класс DTO. [AutoMapper] [automapper] делает это отображение проще.

Поэтому вам нужно выполнить следующие шаги:

  1. Определите типы моделей внутри проекта сервера.
  2. Добавьте эти пакеты в ваш серверный проект как зависимости:
    • AutoMapper
    • AutoMapper.Extensions.Microsoft.DependencyInjection (версия 1.2.0)
  3. Определите MappingProfile внутри проекта сервера и используйте services.AddAutoMapper() в методе ConfigureServices в Startup.cs
  4. Измените методы контроллера для правильного отображения полученных данных и возврата эквивалентного DTO в качестве выходных данных метода.
  5. Параллельно вы можете создать новый проект, содержащий ваши классы DTO. Этот проект является общим для серверного и клиентского проектов.

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