Ребят, ребят, ребят. Я, похоже, сделал это 😄



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



И вылезает типичная проблема: Как изменять поведение общего кода, в зависимости от приложения. Например ui кит, конфигурацию, api сервис и тому подобное



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





//shared module

type Deps = {

apiService: Api

}



const createModule = (deps: Deps) => {

const someFunction = () => {

deps.apiService.getUser();

}

return {someFunction}

}

// application code

createModule({ apiService: new AppApi() }).someFunction()




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



Как всегда в подобных ситуациях, я начал смотреть в сторону Dependency Injection контейнеров. Перепробовав всё, что есть на рынке, понял, что всё сделано дико не удобно.



Устаревшие декораторы, классы, ключи, всё плохо код сплитится и чанкуется. Короче беда бедой.



Реально нормальный туллинг для инверсии зависимостей в typescript – это на данный момент не решённая проблема.



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



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



И к вечеру я просто выпал. Совершенно того не желая, я походу сделал идеальный DI. Вдохновившись Reader монадой + добавив возможность прямого взаимодействия модулей для удобства получилось:



- Сделать полноценный DI с возможностью как связывать модули по интерфейсу, так и делать прямое взаимодействие, но с авто резолвом зависимостей

- Он получился супер маленьким (один файл)

- Никаких классов и декораторов. Просто функции

- Всё строго типизировано

- Кодсплитится

- Не много бойлерплейта



Последнее очень важно. Во всех реализациях без декораторов много бойлерплейта. Это то, что я никак не мог побороть эти 3 года. Но, похоже, что получилось. (Готового для показа кода нет, чуть подождите)



Понятное дело, что нужно ещё тестировать много, и попробовать в разных кейсах. Но, повторюсь: Я уже 3 года пытаюсь сделать нормальный DI. Я знаю все проблемы и возможные подводные камни наизусть. То, что получается, выглядит слишком хорошо.





В общем, ребят, сегодня все мои планы немного меняются. В ближайшее время сделаю видос на youtube c демонстрацией и npm пакет.



Всем благ! Желаю вам инжинирить, да не оверинжинирить