Новый подход к архитектуре внешнего интерфейса javascript.

Дизайн, управляемый доменом, существует уже некоторое время. Это был самый большой ажиотаж вокруг программирования, который я лично могу вспомнить. Но что это?

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

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

Итак, в первую очередь нам нужно начать с наших моделей. Мы собираемся создать раздел заказа и услуги, чтобы донести суть самым простым способом. Мы собираемся начать с базовых классов POJO и двигаться дальше. Мы начнем с класса Service, так как он будет точкой входа для размещения заказа.

class Service {
  constructor(service) {
    this.name = service.name;
    this.cost = service.cost;
    this.maxRequests = service.max;
    this.currentRequests = service.current;
  }
requestService(serviceApprovedCallback) {
    if(this.currentRequests < this.maxRequests) {
      serviceApprovedCallback(this);
    }
    else {
      throw "Service max reached.";
    }
  }
}

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

Далее мы создадим класс Order:

class Order {
  constructor() {}
  placeOrder(service) {
    // SEND ORDER REQUEST TO SERVER
  }
}

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

const orderService = new Order();
const washAndIron = new Service({  
  name: "wash and iron",
  cost: "$2.50",
  max: 10,
  current: 9
})
washAndIron.requestService(orderService.placeOrder);

В качестве альтернативы метод, который я предпочитаю использовать, представляет собой реализацию eventBus, которая позволит нам выполнять несколько процессов с более несвязанной реализацией.

class EventBus {
  constructor() {
    this.subscriptions = [];
  }
  subscribe(event, action) {
    this.subscriptions.push({ event, action });
  }
  emit(event, payload) {
    const subscribers = this.subscriptions.find(sub => {
      return sub.event === event;
    })
    subscribers.forEach(sub => {
      sub.action(payload);
    })
  }
}

Теперь у нас есть очень простой eventBus, который позволит нашим другим доменам запускаться всякий раз, когда эти события генерируются. Итак, чтобы это работало, все, что нам нужно сделать, это обновить наш класс Service до следующего.

class Service {
  constructor(service, eventBus) {
    this.name = service.name;
    this.cost = service.cost;
    this.maxRequests = service.max;
    this.currentRequests = service.current;
   
    this.eventBus = eventBus
  }
  
  requestService() {
    if(this.currentRequests < this.maxRequests) {
      this.eventBus.emit('service-accepted', this);
    }
    else {
      throw "Service max reached.";
    }
  }
}

И, наконец, нам нужно изменить наш класс заказа на это:

class Order {
  constructor(serviceBus) {
    this.serviceBus = serviceBus;
    this.serviceBus.subscribe('service-accepted', this.placeOrder);
  }
  placeOrder(service) {
    // SEND ORDER REQUEST TO SERVER
  }
}

Теперь наша реализация будет просто передавать служебную шину каждому экземпляру, и поэтому вся наша логика будет более связанной.

const eventBus = new EventBus();
const orderService = new Order(eventBus);
const washAndIron = new Service({  
  name: "wash and iron",
  cost: "$2.50",
  max: 10,
  current: 9
}, eventBus)
washAndIron.requestService(); // emits event to listeners in order

Резюме

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

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