Обновить
16K+
49
Alex Gusev@flancer

Я кодирую, потому что я кодирую…

3,1
Рейтинг
99
Подписчики
Отправить сообщение

В статье не увидел, как вы работаете с компонентами: доменная архитектура или что-то ещё.

Если вы имеете в виду DDD, то у меня нет отдельного независимого слоя, отвечающего за бизнес-логику. Каждый npm-пакет (плагин) имплементирует свою часть общей задачи (типа: заказы, клиенты, складской учёт, но на гораздо более мелком уровне). А внутри плагина бизнес-логикой занимаются модели, обработчики и действия (./Mod, ./Web/[Api|Event|Socket], ./Act). Подобный подход можно увидеть в архитектуре платформы Magento (я довольно долго на ней сидел - лет 10, наверное).

Бэк вообще на php. 

Я от этого ушёл в ноду.

Поэтому у меня работа с api сервера представляется как правило отдельным модулем и является по факту отдельным сервисом, который импортируется во фронтенд-проект.

Это одна из причин, почему я ушёл в "один язык для фронта и бэка".

Если изменения несовместимы с предыдущей версией модуля, то vscode это подсветит. Потому что на фронте обязательно ведётся контроль типов с помощью ts.

Я делаю примерно то же самое через JSDoc и PhpStorm :)

А если использовать JSDoc, то тоже нет возможности проверять?

Спасибо за развёрнутый комментарий, коллега. Я думаю, что у меня функционал, реализуемый в вашем коде типа "Сервис", реализуется в типах "Mod" и "Web_[Event|Rtc|Socket]". Тип "Mod" также использует единый "главный сервис" (TeqFw_Web_Api_Front_Web_Connect) для общения с бэком в синхронном режиме (запрос-ответ), остальные - для асинхронного (три отдельных "главных сервиса", по одному на каждый канал). В принципе, на фронте каналы "Web_[Event|Rtc|Socket]" работают как раз по типу "event bus".

Но мне кажется, что на фронте упор в разработке идёт больше на работу с UI, чем с хранилищами браузера или взаимодействие с внешними сервисами (включая бэк). Да и визуально пользователями фронт-приложение воспринимается именно уже в отрендеренном состоянии. Применяя "принцип Парето", я бы сказал, что 80% фронта - это UI и только 20% - передача данных между UI и всем остальным. Так что мне только в страшном сне может привидиться необходимость сменить библиотеку рендеринга (ветка кода "Front_Ui"). У меня в UI-компонентах довольно большой объём кода.

В своих проектах я жёстко завязан на Vue + Quasar. Пробовал другие библиотеки компонентов для Vue - резкий откат по производительности на начальном этапе. В принципе, можно вместо Vue использовать любой "реактивный движок", который не требует транспиляции исходников, но я уже пророс во vue. Но даже при смене "движка" UI-компоненты всё равно будут находиться во "Front_Ui", просто код внутри файлов будет слегка другой :)

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

Спасибо, коллега! Я тоже думаю, что писать фронт и бэк на одном языке (причём без транспиляции), а также использовать DI в JS - это норм (y) Но некоторые тут уверены в обратном. Я просто перестраховался :)

Ну, теперь уже не никто :)

Походу, есть у меня ответ на свой же вопрос. Попросил я тут на днях ChatGPT nodejs кода сгенерировать, чтобы самому не писать. Обратил внимание, что он использует CJS-нотацию вместо ESM. Оно и понятно - в интернетах этого кода тупо больше, а LLM ориентируется на частоту употребления токенов в текстах и если ей не задать впрямую требование использовать ESM, то она выдаёт CJS.

Это статья про бэкенд. Node понимает ESM c 2019-года (с 2017-го в виде экспериментальной фичи). Я начал плотно изучать "JS на бэке" примерно в 2018-м, для меня было вполне естественным использовать самые новые возможности языка (я верю в прогресс, а не в регресс). Отсюда и мой вопрос - чем в 2024-м году на бэке CJS лучше ESM?

Это обучающий материал, который показывает начинающим "как надо". Хотелось бы понять, какие есть аргументы в пользу CJS на бэке за исключением "всегда так делали" и "работает - ничего не трогай"?

А ведь у JS есть ещё "суперсет" - TS, который может транспилировать код и туда, и туда.

const { Pool } = require('pg');

Интересно, а что мотивирует использовать CJS в 2024-м году, через 9 лет после появления ESM?

а сделав мини-приложения, способные даже рендерить страницы в ближайшей к пользователю точке.

Следующий этап - рендерить страницы непосредственно в устройстве пользователя.

Я стараюсь не использовать статических импортов, а вместо этого использую namespace'ы. Из имени интерфейса (Fl32_Auth_Back_Api_Mod_User) в месте его применения разработчику понятно, где искать объявление самого интерфейса. IDE же сканирует JSDoc'и в исходниках и составляет свою таблицу имён классов/интерфейсов/т.п. с привязкой к файлам и ей всё равно, как называются соответствующие объекты кода.

По большому счёту, код интерфейса нужен только на момент написания/редактирования кода, чтобы IDE контролировала его имплементации и показывала их взаимосвязь с интерфейсом, облегчая разработчику навигацию по коду. У меня это не исполняемый файл, а документ в стиле JS (@flancer32/teq-ant-auth/src/Back/Api/Mod/User.js):

/**
 * An interface for an application specific model of the user.
 * This interface is used by `Fl32_Auth_Back_Mod_Session` to retrieve the app specific user profile.
 *
 * @interface
 */
export default class Fl32_Auth_Back_Api_Mod_User {

    /**
     * Read the application-specific data from storage (RDb, ...) to use on the backend.
     *
     * @param {TeqFw_Db_Back_RDb_ITrans} [trx]
     * @param {number} userBid
     * @returns {Promise<{profileBack: *, profileFront: *}>}
     */
    async readProfiles({trx, userBid}) {}

    /**
     * Load app specific user data from a storage (RDb, ...).
     * @param {TeqFw_Db_Back_RDb_ITrans} [trx]
     * @param {*} userRef - app-specific identifier for the user (email, uuid, ...) or some object
     * @returns {Promise<{bid:number, dbUser: Fl32_Auth_Back_Store_RDb_Schema_User.Dto}>}
     */
    async read({trx, userRef}) {}
}

Его нет нужды куда-либо импортировать в виде исходника, это просто документ. Я думаю, что можно было бы объявить аналогичный интерфейс и чисто в JSDoc'ах так, чтобы его понимали и разработчики, и IDE, но это будет более многословно, чем в JS.

Кстати, насколько мне известно, интерфейсы в TS через транспиляцию не проходят и в готовый JS-код не попадают. Так что они тоже в своём роде только для документации.

Не использую. Спасибо за наводку, посмотрю, что она может в IDEA.

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

Я как раз пытался уйти от привязки к физической адресации (пакет:путь/к/файлу/в/пакете) и перейти к логической адресации Vendor_Project_Plugin_Area_Service. Для меня было важно уйти от статического связывания файлов в момент написания кода и перейти к динамическому связыванию в момент выполнения. При внедрении зависимостей через конструктор всё равно нужно преобразовать идентификатор зависимости в путь к конкретному экспорту внутри es-модуля с учётом условий проекта, в котором код выполняется. При логической адресации не так странно, что получаешь имплементацию в месте внедрения интерфейса.

Объявляешь в базовом модуле зависимость от базового интерфейса:

export default class Fl32_Auth_Back_Web_Api_Password_Salt_Read {
    /**
     * @param {Fl32_Auth_Back_Api_Mod_User} modUser
     */
    constructor(
        {
            Fl32_Auth_Back_Api_Mod_User$: modUser,
        }
    ) {}
}

А по факту Контейнер Объектов внедрит имплементацию этого интерфейса в соотв. проекте. Типа такой:

/**
 * @implements Fl32_Auth_Back_Api_Mod_User
 */
export default class Lp_Desk_Back_Di_Replace_Fl32_Auth_Back_Api_Mod_User {...}

Ну и в случае привязки к именам пакетов и файлу внутри конструктор выглядел бы чуть чудовее:

/**
 * @param {Fl32_Auth_Back_Api_Mod_User} modUser
 */
constructor(
    {
        '@flancer32/teq-ant-auth/src/Back/Api/Mod/User.mjs#default': modUser,
    }
) {}

В общем, importmap хорош для статических импортов. В динамических уже можно вычислять самому путь к нужному ресурсу, а в моём DI он идёт как пятое колесо в телеге.

Принцип importmap'а, по большому счёту, совпадает с сопоставленим логического namespace'а физическому пути к файлу в моём Контейнере. Только importmap делает это сопоставление на уровне браузера, а я - на уровне Контейнера. Но перед началом работы и то, и то нужно конфигурить.

Да, namespaces - это отличный инструмент для организации кода. В Java он был с самого начала (1996), в PHP добавили в версии 5.3 (2009), в JS их упорно путают с областью действия/видимости (scope). Я у себя в коде не использую importmap на уровне браузера, вместо этого делаю аналогичный маппинг на уровне Контейнера Объектов (одинаково работает и в браузере, и в ноде). Когда я начинал его делать в 2019-м, importmap'ов ещё не было, они где-то с 21-го года начали появляться в браузерах (в Safari так вообще с 2023-го). Возможно, с ними у меня получилось бы по-другому, но как есть. На данный момент я не вижу смысла использовать у себя importmap. У меня этот функционал встроен в Контейнер Объектов.

cacheStorage + Service Worker даёт возможность работать веб-приложению в режиме offline. Это из области PWA для мобильных в областях с пониженным качеством интернета.

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

Да, так даже лучше - интерпретация сигнала. Кому-то негативная, а кому-то позитивная. Вспомнил книгу про Джеймса Бонда - Казино "Рояль". Пытка со стулом. Там была мысль, что если долго пытать человека, он начинает воспринимать боль по-другому. Он привыкает к ней и, в качестве защиты, начинает наслаждаться. Одни и те же сигналы сначала вызывают негатив, а затем позитив. Типа, если ничего не можешь сделать, то поменяй своё отношение к происходящему. Так что интерпретация - это даже точнее, чем просто сигнал.

Боль - это просто сигнал. Животное электричество, которое бежит по нервам. Мы в норме воспринимаем боль, как негатив, потому что это, как правило, сигнал о наносимых нашему телу повреждениях. Мазохисты же воспринимают этот же сигнал несколько по-другому. Удовольствие, получаемое ими от этого сигнала, зачастую превышает уровень повреждений. А может даже и всегда - я не в курсе. Я просто знаю, что этот сигнал можно игнорировать в некоторой степени.

Искренне надеюсь, что на Хабре это был технический сбой, а не разумная деятельность. Первый раз столкнулся, что минимум три коммента исчезли, оставив только следы в почтовых ящиках.

В общем, я у себя и Ambient Context использую, но только там, где не имею возможности работать с конструктором объекта напрямую. Например, я таким образом внедряю у себя во все vue-компоненты имя namespapce'а в котором они определяются, для последующего использования с i18next (нахождение пакета с переводами).

    return {
        teq: {package: DEF.SHARED.NAME},
        name: NS,
        template,
        components: {},
        data() {
            return { };
        },
        computed: {},
        ...
    };

Это создание шаблона vue-компонента для последующего конструирования отдельных экземпляров на его основе. Свойство teq - нестандартное для vue, я через него передаю в шаблон всю нужную мне информацию, которая становится доступной в каждом экземпляре через this.$options.teq

Но это об безысходности - я не разработчик vue, а это был самый лёгкий способ внедриться, пусть и не самый правильный. Так и осталось.

Я так понимаю, что одна звёздочка этой "глупой книге" на Амазоне от вас прилетела :)

Лично мне "IoC через DI" более чем удобно. Я и до середины возможностей этой технологии пока не добрался. Может быть потом, когда уткнусь в её ограничения, я переосмыслю накопленный опыт и перейду к "Контексту Окружения", но пока так.

Информация

В рейтинге
1 300-й
Откуда
Рига, Латвия, Латвия
Дата рождения
Зарегистрирован
Активность

Специализация

Фулстек разработчик
Ведущий
От 3 000 €
JavaScript
HTML
CSS
Node.js
Vue.js
Веб-разработка
Progressive Web Apps
PostgreSQL
MySQL
GitHub