Как абстрагироваться и унифицировать различные API CRUD

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

У нас есть N разных API, которые используют более или менее одни и те же ресурсы. Например, есть ресурс для создания пользователя. Для одного API X ресурс называется «пользователь», для другого API Y его «пользователи», и всем им требуются разные поля для запроса на создание. X требует только имя и адрес в виде строки, в то время как Y требует имя в виде строки, но адрес в виде объекта, состоящего из улицы, города и страны.

Мое первое намерение состояло в том, чтобы использовать шаблон адаптера и определить мой собственный класс User вместе со специфическими интерфейсами API.

interface XUser {
   name: string;
   address: string;

   CREATE() : XUserWithId
}

interface YUser {
   name: string;
   address: {
      street: string;
      city: string;
      country: string
   };
   
   CREATE() : YUserWithId
}

interface IUser {
   name: string;
   street: string;
   city: string;
   country: string;
}

class User implements IUser {}

Затем я хотел написать свои адаптеры, чтобы мой пользовательский класс вел себя как объекты, специфичные для API:

class XUserAdapter implements XUser {
   constructor(user: IUser) {
      this.name = user.name;
      this.address = [user.street, user.city, user.country].join(", ");
   }
}


class YUserAdapter implements YUser {
   constructor(user: IUser){
      this.name = user.name;
      this.address = {
         street: user.street;
         city: user.city;
         country: user.country;
      }
   }
}

СОЗДАНИЕ пользовательского ресурса в API X и Y теперь сводится к созданию одного пользовательского объекта, помещению его в адаптеры и вызову функций CREATE адаптеров:

users: User[] = [];

tom = new User(name = "Tom", street = "Foostreet", city = "Bartown", country = "USA");

xuser = new XUserAdapter(tom);
yuser = new YUserAdapter(tom);

xuser = xuser.CREATE();
yuser = yuser.CREATE();

Моя проблема в том, что, очевидно, вызов CREATE вернет мне пользовательские объекты платформы, которые я хочу иметь в форме моего собственного класса User. Для этого потребуется еще два адаптера для «перевода» в противоположном направлении:

class UserFromYUser implements IUser {
   constructor(yUser: YUser) {
      ...
   }
}

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

Заранее спасибо.

Повышение качества Laravel с помощью принципов SOLID: Лучшие практики и примеры
Повышение качества Laravel с помощью принципов SOLID: Лучшие практики и примеры
Когда мы говорим о том, как сделать следующий шаг в качестве разработчика, мы должны понимать, что качество кода всегда является основным фокусом на...
Принципы SOLID - лучшие практики
Принципы SOLID - лучшие практики
SOLID - это аббревиатура, обозначающая пять ключевых принципов проектирования: принцип единой ответственности, принцип "открыто-закрыто", принцип...
2
0
179
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Похоже, шаблон репозитория — это путь. Как говорит martinfowler.com:

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

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

Это пример кода C#:

public interface IRepository<T>
{
    void Insert(T entity);
    void Delete(T entity);
    void Update(T entity);
    IQueryable<T> GetAll();
    T GetById(int id);
}

Благодарю за ваш ответ! Действительно, выглядит очень многообещающе. Я закодировал несколько тестов, и мне кажется, что репозиторий, по крайней мере, дает общее место для всех адаптеров. Отличный намек!

Finkle MacGraw 11.10.2022 13:59

Я отметил ваш ответ как принятый, поскольку, вероятно, нет «верного» ответа на такой вопрос. Если углубиться в проблему, шаблон репозитория предлагает хороший уровень абстракции, чтобы скрыть индивидуальную сложность API.

Finkle MacGraw 12.10.2022 16:39

Другие вопросы по теме