Подтвердить что ты не робот

Node Модульная архитектура

Я создаю приложение nodejs, которое сейчас довольно велико. Стремясь избежать монолитного приложения node, я пошел по архитектурному пути более модульной системы, разбив несколько компонентов на отдельные модули npm. Они публикуются с использованием npm и устанавливаются в зависимых модулях. У меня около 6 различных модулей (которые я хотел бы разразиться больше), и теперь стало сложно управлять пакетами.

Проблемы:

  • Существует вложенная зависимость, поэтому, если я изменяю модуль A, а модуль B зависит от модуля A, а модуль C зависит от модуля B, то при обновлении модуля AI необходимо опубликовать его новую версию, что означает, что мне нужно обновить это в модуле B, что означает, что мне также нужно опубликовать это, а затем, наконец, мне нужно установить эту новую версию в модуле A... вы можете увидеть, где это может быть болью. Более того, обновление версий во всем пакете. Json является ручным и поэтому подвержено ошибкам, и ожидание каждой публикации требует много времени.
  • Модули могут совместно использовать зависимости npm, поэтому иногда конфликты возникают, когда пакеты обновляются. Чем больше модулей, тем выше вероятность конфликта.

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

Возможные решения:

  • Монолит. Управление зависимостями как единое приложение в одном репозитории с каждым модулем, который становится сервисом. Это означает, что требуется только одно обновление, и весь модуль apis будет синхронизирован. Тем не менее, обращение к библиотекам в коде может быть немного больным (так как я полагаю, что им нужно будет ссылаться относительно локального файла), я не уверен, как можно обеспечить структурную иерархию между модулями и повторное использование кода быть сложнее с модулями вне репозитория.

  • Микросервисы. Чтобы каждый модуль обслуживал микросервис. Это сохраняет все преимущества модульной системы, но я обеспокоен тем, что она добавит много сложности в сборку и управление всеми сервисами, которая сама по себе станет полноценной работой.

  • Продолжайте движение. Выработайте способ сохранить текущую архитектуру, но устраните проблему с нажатием обновлений и т.д. Возможно, скрипты для обновления версий и сжатия для обеспечения правильных зависимостей. Я думаю, что это было бы трудно и потенциально могло бы привести его к монолитной системе другого сорта.

Вариант 1 кажется мне наиболее управляемым, но я не хочу потерять модульную структуру, если мне это не нужно.

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

Спасибо

4b9b3361

Ответ 1

Я бы рекомендовал пойти на решение 2.

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

Две описанные вами проблемы просто вызваны тем, что каждый модуль независимо хранится в виде пакета npm.

Преимущества

  • Проблема 1 решена, так как вам больше не нужно управлять пакетами npm в package.json.
  • Проблема 2 решена, поскольку у вас есть только один package.json, управляющий всеми зависимостями
  • У вас все еще есть чистая модульная структура благодаря использованию отдельных модулей node.js.

Пример реализации

Несколько месяцев назад я реорганизовал монолитное приложение node.js, используя эти принципы, и это действительно облегчило обслуживание и не добавило накладных расходов на процесс сборки.

Шаблон следующий:

Основной модуль app.js

var sys = require('sys')
    , events = require('events')
    , UserModel = require('./model/user') // this is a microservice taking care of managing user data
    , Logic = require('./controller/logic')   // this is another microservice doing some work

var App = function (server, app) {

    this.controller = (
        logic: new Logic(this) // "this" is the app instance, it passed to all microservices, which will benefit from the access to event emitter...
    }
    this.model = {
        new UserModel(this)
    }

    // ...
}

sys.inherits(App, events.EventEmitter)

module.exports = App

Микросервис выглядит следующим образом:

/**
* Logic functions
* 
* this controller does ...
*
* @constructor
*
*/
function Logic(app) {

    /****************************************
    *
    * Private methods
    *
    ****************************************/

    /**
    * this is a private function in the controller
    * 
    * @private
    */
    function computeSomething () {

        var res = 3

        app.emit('computed', res) // emit event, that can be liseted to by some other modules

        return res
    }


    /****************************************
    *
    * Public methods
    *
    ****************************************/    

    /**
    * 
    * This function can be called directly by the other modules using "app.controller.logic.work()"
    * 
    */
    this.work = function () {

        return 'result is ' + computeSomething()
    }


    /****************************************
    * 
    * Event listeners
    * 
    ****************************************/

    /**
    * listener: event from the app - loose-coupling magic happens thanks to this. Recommended over public functions.
    *
    * @private
    */
    app.on('data-ready', this.work)

}

module.exports = Logic

Ответ 2

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

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

диаграмма текущей структуры

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

Преимущество вашей текущей структуры в том, что у нее нет ацилических зависимостей. Если вы обновляете модуль B, вы точно знаете, как работает модуль C, и ничего не меняется с модулем A. Он должен оставаться таким образом.

Управление зависимостями

Структура зависимостей

Существуют принципы проектирования пакетов, которые напрямую связаны с вашими проблемами:

Принцип стабильных зависимостей
Зависит в направлении устойчивости

Учитывая исходную структуру C -> B -> A, большинство ваших изменений должно произойти в C и A не должно быть никаких причин для изменения.

Принцип стабильных абстракций
Пакет должен быть таким абстрактным, как стабильный

Относится к предыдущему принципу. Абстрактные классы не учитывают конкретные реализации, и есть много способов сделать это с помощью Javascript.

Если вы хорошо следуете этим принципам, вы можете обнаружить, что это не проблема чтобы иметь более трех пакетов, потому что пакеты более низкого уровня не будут сильно меняться.


Что должно быть в пакете?

Пакет по функциям

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

Лучшим способом организации ваших пакетов будет Package by Feature. Теперь расслоение не так уж плохо, и когда вы упаковываете по функциям, вы все равно должны иметь эту многоуровневую структуру.

Ответ 3

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

Список модульных принципов

  • Код физически организован в папках по функциям.
  • Код делает одно и только одно, и он делает это очень хорошо.
  • Каждая функция может быть добавлена ​​или удалена в любое время, поскольку она не имеет зависимости от других функций.
  • Все сообщения с другими модулями не производятся напрямую. Вместо этого используется песочница или промежуточное программное обеспечение.
  • Если для нескольких модулей требуется общая функциональность, они берут ее из верхней иерархической полунепрерывной() структуры.

Преимущества и недостатки Этот подход рассматривает одну конкретную цель: свободную связь. Идея заключается в том, что каждый модуль может быть реализован сам по себе, его можно разрабатывать и тестировать индивидуально, и многие люди могут вносить вклад одновременно с функциями. Посмотрите на WordPress или экосистему node. Возьмите этот пример и переместите его в свой проект.

Один пример

CSS явился для вас примером того, как этот модульный подход может работать. Если у вас есть сайт со многими страницами и многие разделы в нем, и ваш клиент хочет, чтобы в каждом разделе был вариант внешнего вида, возможно, в конечном итоге вы получите несколько сотен определений CSS в одном минифигурированном большом файле CSS.

Управление этим CSS может потребовать переменные, препроцессор, PostCSS, обработку Javascript.... но реальный смысл в том, что вы никогда не будете использовать больше нескольких определений CSS на каждой странице.

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

Предложение структуры папок Я работаю над тем, что весь код должен быть организован одинаково. Основные папки:

  • core
  • расширения
  • двигатели

Внутри каждой такой структуры: ядро ​​
----- менеджер-продюсер
------------ css
------------ img
------------ СМИ
------------ js
------------ просмотр
------------ db
------------ данные
------------ aux
------------ config.json
------------ README.txt
------------ settings.txt
----- песочница
------------ css
------------ img
------------ СМИ
------------ js
------------ просмотр
------------ db
------------ данные
------------ aux ------------ config.json
------------ README.txt
------------ settings.txt
----- global
------------ css
------------ img
------------ СМИ
------------ js
------------ просмотр
------------ db
------------ данные
------------ aux
------------ config.json
------------ README.txt
------------ settings.txt
Расширения
----- встречи
------------ css
------------ img
------------ СМИ
------------ js
------------ просмотр
------------ db
------------ данные
------------ aux
------------ config.json
------------ README.txt
------------ settings.txt
----- календарь
------------ css
------------ img
------------ СМИ
------------ js
------------ просмотр
------------ db
------------ данные
------------ aux
------------ config.json
------------ README.txt
------------ settings.txt
----- Акции
------------ css
------------ img
------------ СМИ
------------ js
------------ просмотр
------------ db
------------ данные
------------ aux
------------ config.json
------------ README.txt
------------ settings.txt

Надеемся, что эти идеи помогут, любые комментарии приветствуются.