Skip to content

Контейнер зависимостей

@vue-modeler/dc — это контейнер зависимостей на основе shared composable.

Контейнер решает проблему управления жизненным циклом моделей и сервисов:

  • Упрощает совместное использование моделей и сервисов между компонентами
  • Отделяет бизнес-логику от представления
  • Позволяет реализовать принципы MVVM, DDD, SOLID

Основные возможности

  • Ленивая загрузка: создает зависимости только когда они нужны
  • 🗑️ Автоматическое удаление: удаляет неиспользуемые зависимости
  • 🔧 Поддержка destructor: автоматически вызывает метод destructor при очистке
  • 💾 Постоянные экземпляры: позволяет создавать долгоживущие сервисы

TIP

Контейнер зависимостей хранит зависимости, НО не поддерживает автоматического внедрения (autowire). Разработчик самостоятельно внедряет зависимости в отдельном модуле или слое.

Как это работает?

Контейнер работает по принципу "создай по требованию, удали когда не нужно":

  1. Регистрация фабрики — вы регистрируете фабрику для создания экземпляра, получаете shared composable
  2. Создание экземпляра — экземпляр создается только при первом обращении
  3. Переиспользование — при повторных обращениях возвращается существующий экземпляр
  4. Отслеживание ссылок — контейнер считает, сколько компонентов используют экземпляр
  5. Очистка — когда счетчик использования становится 0, экземпляр удаляется

Регистрация фабрики

provider регистрирует фабрику зависимости и создает shared composable, который будет использоваться в компонентах.

Фабрика — простая функция, которая может возвращать значение любого типа.

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

typescript
import { provider } from '@vue-modeler/dc';

const useDependency = provider(() => {
  // ваша фабрика по созданию экземпляра
  return {
    // экземпляр с методами и данными
  };
});


// так тоже можно
const useSymbol = provider(() => new Symbol('dependency'));
const useNumber = provider(() => 10);
const useTrue = provider(() => true);

// передаем зависимости в конструктор
const useObject = provider(() => new SomeModel(
  useDependency(),
  useSymbol(),
  useNumber(),
  useTrue()
));

Использование в компонентах

Пример использования провайдера внутри шаблона компонента:

html
<template>
  <div>{{ model.state }}</div>
</template>

<script setup lang="ts">
import { useDependency } from '@/providers/myDependency';

const model = useObject(); // получаем экземпляр
</script>

Постоянные экземпляры

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

Для этого нужно передать опцию persistentInstance: true в функцию provider.

typescript
const usePersistentService = provider(
  () => new MyService(),
  { persistentInstance: true } // дополнительные опции
);

Основные особенности постоянных экземпляров:

  • Сохраняются в контейнере даже после освобождения всей области видимости
  • Сохраняют своё состояние между перезагрузками компонентов
  • Вложенные провайдеры становятся постоянными автоматически, если находятся внутри постоянного провайдера
  • Полезны для сервисов уровня приложения, кэшей и менеджеров состояния

Например, вот как выглядит использование вложенных провайдеров:

typescript
// вложенный провайдер становится постоянным вместе с основным
const useNestedService = provider(() => new NestedService());

const usePersistentService = provider(
  () => new MainService(useNestedService()),
  { persistentInstance: true }
);

WARNING

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

Для SSR постоянные экземпляры безопасны, так как при каждом запросе создается новый экземпляр контейнера, а старый удаляется вместе с содержимым.

Released under the MIT License.