Ребят, ребят, ребят. Я, похоже, сделал это 😄
История. У нас на проекте монорепа. Несколько приложений, которые переиспользуют большие куски логики. И чем дальше в лес, тем большие куски нужно переиспользовать.
И вылезает типичная проблема: Как изменять поведение общего кода, в зависимости от приложения. Например ui кит, конфигурацию, api сервис и тому подобное
Есть типичное решение – инверсия зависимостей. Когда модуль определяет типы, которые должно реализовать приложение. И использует эти типы в коде
И это хорошо работает для небольших модулей. Но, представьте, у нас не 1 файл, а тысячи. Большие развесистые зависимости. В этот момент начинается очень много проблем с разрешением этих зависимостей и раскладыванием их в нужных местах
Как всегда в подобных ситуациях, я начал смотреть в сторону Dependency Injection контейнеров. Перепробовав всё, что есть на рынке, понял, что всё сделано дико не удобно.
Устаревшие декораторы, классы, ключи, всё плохо код сплитится и чанкуется. Короче беда бедой.
Реально нормальный туллинг для инверсии зависимостей в typescript – это на данный момент не решённая проблема.
Я много лет пытался придумать решение этой проблемы, но всегда получалась какая то дичь.
Понимая, что в общем виде эта задача нормально не решается, попробовал сделать узкое решение, для задачи абстрактного модуля.
И к вечеру я просто выпал. Совершенно того не желая, я походу сделал идеальный DI. Вдохновившись Reader монадой + добавив возможность прямого взаимодействия модулей для удобства получилось:
- Сделать полноценный DI с возможностью как связывать модули по интерфейсу, так и делать прямое взаимодействие, но с авто резолвом зависимостей
- Он получился супер маленьким (один файл)
- Никаких классов и декораторов. Просто функции
- Всё строго типизировано
- Кодсплитится
- Не много бойлерплейта
Последнее очень важно. Во всех реализациях без декораторов много бойлерплейта. Это то, что я никак не мог побороть эти 3 года. Но, похоже, что получилось. (Готового для показа кода нет, чуть подождите)
Понятное дело, что нужно ещё тестировать много, и попробовать в разных кейсах. Но, повторюсь: Я уже 3 года пытаюсь сделать нормальный DI. Я знаю все проблемы и возможные подводные камни наизусть. То, что получается, выглядит слишком хорошо.
В общем, ребят, сегодня все мои планы немного меняются. В ближайшее время сделаю видос на youtube c демонстрацией и npm пакет.
Всем благ! Желаю вам инжинирить, да не оверинжинирить ✨
История. У нас на проекте монорепа. Несколько приложений, которые переиспользуют большие куски логики. И чем дальше в лес, тем большие куски нужно переиспользовать.
И вылезает типичная проблема: Как изменять поведение общего кода, в зависимости от приложения. Например 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 пакет.
Всем благ! Желаю вам инжинирить, да не оверинжинирить ✨