API с полиморфизмом
В этом посте делюсь кратким пересказом главы из книги “API Design Patterns” Дж. Дж. Гивакса, где рассматривается использование полиморфизма в проектировании API.
Зачем это нужно? Полиморфизм в ООП позволяет работать с объектами разных типов через общий интерфейс. Например, для фигур Triangle
и Square
можно реализовать метод countSides()
, который будет работать с общим типом Shape
. Перенос этой концепции в API так же позволяет вместо отдельных эндпоинтов для каждого типа ресурса (например, Triangle
и Square
) создать один метод. Но у этого подхода есть свои сложности и нюансы, особенно в контексте JSON и HTTP. Цель данного паттерна — предложить безопасный способ внедрения полезных аспектов полиморфизма в ресурсно-ориентированные API.
Полиморфные ресурсы идеально подходят, когда объекты имеют схожие черты, как, например, разные типы сообщений в чате (TextMessage
, PhotoMessage
). В таких случаях можно создать обобщённый ресурс Message
, который упростит получение данных через стандартные методы API. Однако если ресурсы имеют разные паттерны взаимодействия (например, ChatRoom
и Broadcast
), то лучше их разделить, чтобы избежать путаницы и сохранить удобство работы с API.
Итак, что важно для реализации? Полиморфные ресурсы обычно имеют поле type
, который позволит определить тип ресурса. Поле должно быть строкой. В качестве типа этого поля может возникнуть соблазн использовать перечисления, но они могут вызывать множество проблем (об этом в отдельном посте расскажу). Изменение названия типа после создания не рекомендуется, чтобы избежать ошибок и нарушений целостности данных.
Для хранения данных полиморфных ресурсов можно использовать надмножество полей, охватывающее все возможные типы. Также можно абстрагировать содержимое в одно поле content
, у которого будет разный тип в зависимости от типа ресурса:
1
2
3
4
5
6
7
interface Message {
id: string;
sender: string;
type: 'text' | 'photo' | 'audio' | 'video';
content: string | Media;
...
}
Не забываем про валидацию! Полиморфные ресурсы требуют разных подходов к валидации данных. Например, для ресурса Message
текстовые сообщения будут содержать текст, а в случае медиа, например с типом photo
, содержимым уже должен быть URI. Важно тщательно валидировать данные, чтобы избежать ошибок, особенно если поля противоречат друг другу (например, если передаются параметры, несовместимые с типом ресурса).
Полиморфные методы, такие как POST /{id=shapes}:countSides
, могут работать одинаково для разных типов фигур, тем самым упрощая взаимодействие с ресурсами. Однако важно быть осторожным с их применением, чтобы не нарушить принципы разделения ответственности.
Почему не стоит использовать полиморфные методы? Стоит быть особенно осторожными с обобщенными методами, типа DeleteResource()
. Такие методы напоминают использование типа any
в аргументах функций, где можно передать значение любого типа - function deleteResource(resource: any): void
. Хотя это упрощает работу с API, оно может привести к проблемам, так как разные типы ресурсов могут требовать разной обработки. Например, метод DeleteResource()
может не учесть различия между жестким и мягким удалением для разных типов. В таких случаях лучше использовать отдельные методы для каждого типа ресурса.
Подытожим. Полиморфизм в API - это крутая штука, которая помогает уменьшить дублирование функциональности. Важно поддерживать стабильность строкового поля type
и правильно валидировать данные. Стоит помнить, что полиморфные методы не всегда оправданы, так как они могут усложнить и ограничить API.