Как я писал фреймворк Dunai
Содержание
Начало
Давным давно, в далекой галактике, при использовании NodeJS джедаю стало сложно следить за кодом и зависимостями.
Но он помнил, он видел магию автоматического внедрения зависимостей, так как был в клане Angular.
И вот, молодой джедай, воодушевленный могуществом TypeScript решил познать черную магию декораторов, он хотел понять как эта магия работает…
И пошел странствовать в поисках мудрости — пошел в гугл, попал на хабр, все узнал.
Конец.
Прелюдия
По моему мнению на чистом JavaScript слишком неудобно писать сложные приложения, и TypeScript позволяет значительно упростить эту задачу.
Меня заинтересовала возможность получения типов параметров конструктора, и пока экспериментировал с декораторами, у меня получился вполне рабочий инжектор зависимостей, и было решено использовать это во благо.
Озарение
Быстро появилось представление, о том как можно упростить написание бекенда на ноде, в результате получилось довольно красивое описание класса приложения:
|
|
Dependency Injection через конструктор, для упрощения тестирования. Минусом является то, что в конструкторе не укажешь интерфейс, а если и укажешь то надо будет явно указывать зависимость, поэтому в тестах приходилось некоторые тестовым зависимостям некрасиво явно присваивать тип any настройка роутинга и контроллеров в классе приложения а не в самом контроллере. Все роуты подключаются в одном месте, что позволяет легко найти нужный контроллер любого роута или поменять пути класс приложения не связан с http сервером, что позволяет, например, слушать на разных портах или же вообще не пользоваться http сервером
В результате стартовый скрипт выглядел довольно просто
|
|
Собственно даже комментировать нечего. В дальнейшем, подключение к базе данных лучше выполнять в async методе класса Application.
Контроллер
Практически всегда разные группы обработчиков запросов клиентов имеют общую кодовую базу и общие зависимости, поэтому логично объединить их в один класс, и через декоратор прописать детали:
|
|
Учитывая строку this.server.registerController('/’, TestController); такое приложение будет обрабатывать следующие пути:
GET /some/
GET /some/view
POST /some/view
Удобно, пути в пределах контроллера прописываются рядом с обработчиком, сами контроллеры прописываются централизованно, под капотом — уже знакомый express. Ну и конечно же DI
Сервис
Сервисы я хотел что бы были тоже с автоматическим внедрением зависимостей (да, да, опыт разработки на Angular сказывается):
|
|
Темная сторона силы
Нормального способа разрешать циклические зависимости я не нашел, однако циклические зависимости — антипаттерн так что это особо и не требуется.
А вот отследить циклическую зависимость надо, но, к сожалению, достоверно не получится в рамках текущей концепции декораторов.
Для работы фреймворка требуются некоторый настройки в tsconfig.json
|
|
Декораторы работают иначе если компилировать в ES3 и ниже, поэтому в опциях компилятора target требуется указывать не ниже ES5.
Также для работы фреймворка требуются Reflect.
Автор Arswarog
Обновлено 3 августа 2018