Комментарии 5
Как я понял, в вашей библиотеке именно клиент зависимости управляет тем, как она будет создана, с помощью вот этой сложной системы идентификаторов. Но мне кажется, что от DI скорее ждут того, чтобы клиент получал уже готовую зависимость. То, как ее можно создавать - фабрикой или конструктором, например - могла бы сообщать контейнеру сама зависимость. А синглтоном она будет или нет, кажется, должно решать само приложение, тот его код и конфигурация, которые собирают все модули воедино.
Более того, насколько вижу, у вас структура этих идентификаторов получается привязанной к пути к файлу с реализацией зависимости - а как при этом зависеть от интерфейса, а не конкретной реализации?
В итоге, ваши примеры кажутся полностью идентичными просто обычным импортам, без каких-либо преимуществ. Мы все так же просто указываем файл с реализацией зависимости и как ее создать - просто теперь почему-то в виде отдельного DSL.
Но мне кажется, что от DI скорее ждут того, чтобы клиент получал уже готовую зависимость. То, как ее можно создавать - фабрикой или конструктором, например - могла бы сообщать контейнеру сама зависимость.
Так и есть. В es6-модуле экспортом может быть что угодно, включая класс и фабричную функцию. Будет ли этот экспорт внедрён as-is или будет внедрён результат выполнения фабричной функции (конструктора класса) - зависит от контейнера. Который конфигурируется при помощи идентификаторов зависимости в месте внедрения зависимости (могла бы сообщать контейнеру сама зависимость). Так в одном случае один и тот же экспорт должен внедряться as-is, в другом - как синглтон, в третьем - как инстанс (можно даже в пределах одного конструктора):
export default class Service {
constructor(
{
'Ns_Module.export': asIs,
'Ns_Module.export$': asSingleton,
'Ns_Module.export$$': asInstance,
}
) {}
}
Тут можно говорить ещё об одном контуре IoC - когда конфигурация контейнера идёт не через сам контейнер, а через клиентов, которые он обслуживает.
Более того, насколько вижу, у вас структура этих идентификаторов получается привязанной к пути к файлу с реализацией зависимости - а как при этом зависеть от интерфейса, а не конкретной реализации?
Вы правы, идентификатор привязывается к пути в исходнику, если исходник существует. Но в моей реализации контейнера после этапа разбора идентификатора (например, Fl32_Auth_Back_Api_Mod_User) идёт этап пред-обработки, где Контейнер может заменить в структуре идентификатора любую из его частей или все сразу:
Если в настройках Контейнера на уровне соответствующего приложения прописать, что такой-то интерфейс должен заменяться такой-то имплементацией, то в таком-то приложении будет использоваться соответствующая имплементация вместо интерфейса.
В саму библиотеку этот функционал не входит. На её уровне реализованы только возможности подключения цепочек препроцессоров и постпроцессоров, а правила пред- и пост-обработки уже закладываются отдельно, в зависимости от приложения и его процессоров.
В итоге, ваши примеры кажутся полностью идентичными просто обычным импортам, без каких-либо преимуществ.
Без пред- и пост-обработки так бы и было. Но теперь связывание выполняется в runtime, а не в коде, и правила связывания могут динамически меняться в зависимости от приложения (подключенных к Контейнеру процессоров). Например, плагин аутентификации, работающий на своём уровне с интерфейсом Fl32_Auth_Back_Api_Mod_User
, в одном приложении получит одну модель пользователя, а в другом - другую. В задачи плагина аутентификации входит связывание этой модели с контекстом HTTP-запроса и его не интересует, что там за модель. Как Стетхем в "Перевозчике" - "Никогда не открывать посылку". Плагин в одном месте запрашивает у приложения модель пользователя, а в другом - предоставляет приложению возможность эту модель забрать. И один и тот же плагин может работать без изменений в разных приложениях, если они придерживаются контракта (имплементируют интерфейс).
Зачем столько сложностей, если всё делается куда проще?
export class $my_app extends $my_base {
do_something() {
const user = this.$.$my_user.create()
this.$.$acme_log_rise( 'User created with id=', user.id )
}
}
Формат описания идентификатора зависимости в JS DI