ExpressJS Как структурировать приложение?

528

Я использую веб-фреймворк ExpressJS для NodeJS.

Люди, использующие ExpressJS, размещают свои среды (разработку, производство, тестирование ...), свои маршруты и т. Д. На app.js. Я думаю, что это не очень красиво, потому что когда у вас большое приложение, app.js слишком велик!

Я хотел бы иметь эту структуру каталогов:

| my-application
| -- app.js
| -- config/
     | -- environment.js
     | -- routes.js

Вот мой код:

app.js

var express = require('express');
var app = module.exports = express.createServer();

require('./config/environment.js')(app, express);
require('./config/routes.js')(app);

app.listen(3000);

конфиг / environment.js

module.exports = function(app, express){
    app.configure(function() {
    app.use(express.logger());
    });

    app.configure('development', function() {
    app.use(express.errorHandler({
        dumpExceptions: true,
        showStack: true
    }));
    });

    app.configure('production', function() {
    app.use(express.errorHandler());
    });
};

конфиг / routes.js

module.exports = function(app) {
    app.get('/', function(req, res) {
    res.send('Hello world !');
    });
};

Мой код работает хорошо, и я думаю, что структура каталогов прекрасна. Тем не менее, код должен быть адаптирован, и я не уверен, что это хорошо / красиво.

Лучше использовать мою структуру каталогов и адаптировать код или просто использовать один файл (app.js)?

Спасибо за ваши советы!

Сандро Мунда
источник
Проблемы с производительностью при таком способе все еще скрываются? Я помню, как читал где-то (может быть, экспресс-группу), что, когда вы разделяете все, как это, вы теряете тонну производительности. Что-то вроде ваших запросов / сек упадет на заметную величину, почти как если бы это была ошибка.
AntelopeSalad
2
Это было из группы Экспресс Google. Вот ссылка: groups.google.com/group/express-js/browse_thread/thread/…
AntelopeSalad,
52
Нет, это очень не соответствует действительности
tjholowaychuk
github.com/rahulshukla-rs/…
Рахул Шукла

Ответы:

306

Хорошо, это было давно, и это популярный вопрос, поэтому я решил создать репозиторий github для скаффолдинга с кодом JavaScript и длинным README о том, как мне нравится структурировать приложение express.js среднего размера.

focusaurus / express_code_structure - это репозиторий с последним кодом для этого. Потяните запросы приветствуются.

Вот снимок README, так как stackoverflow не любит ответы типа «просто ссылка». Я сделаю некоторые обновления, так как это новый проект, который я буду продолжать обновлять, но в конечном итоге репозиторий github станет актуальным местом для получения этой информации.


Экспресс структура кода

Этот проект является примером того, как организовать веб-приложение express.js среднего размера.

Текущий как минимум экспресс v4.14 декабрь 2016

Статус сборки

JS-стандарт стиль

Насколько велико ваше приложение?

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

Если ваше приложение маленькое, вам не нужна такая глубокая структура каталогов, как показано здесь. Просто сделайте это просто и вставьте несколько .jsфайлов в корень вашего хранилища, и все готово. Вуаля.

Если ваше приложение огромно, в какой-то момент вам нужно разбить его на отдельные пакеты npm. В целом подход node.js предпочтителен для множества небольших пакетов, по крайней мере, для библиотек, и вы должны создать свое приложение, используя несколько пакетов npm, поскольку это начинает иметь смысл и оправдывает накладные расходы. Так как ваше приложение растет, и некоторая часть кода становится многократно используемой за пределами вашего приложения или становится чистой подсистемой, переместите его в свой собственный репозиторий git и превратите в отдельный пакет npm.

Таким образом, цель этого проекта - проиллюстрировать работоспособную структуру для приложения среднего размера.

Какова ваша общая архитектура

Существует много подходов к созданию веб-приложения, таких как

  • Серверная сторона MVC а-ля Ruby on Rails
  • Стиль одностраничного приложения в стиле MongoDB / Express / Angular / Node (MEAN)
  • Основной веб-сайт с некоторыми формами
  • Модели / Операции / Виды / Стиль событий а-ля MVC мертв, пора MOVE на
  • и многие другие, как современные, так и исторические

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

  • На сайте есть несколько традиционных статических страниц / шаблонов.
  • «Приложение» часть сайта разработана как одностраничный стиль приложения.
  • Приложение предоставляет браузеру API в стиле REST / JSON.
  • Приложение моделирует простой бизнес-домен, в данном случае это приложение для автосалона.

А как насчет Ruby on Rails?

На протяжении всего проекта будет темой, что многие идеи, воплощенные в Ruby on Rails, и принятые ими решения "Convention over Configuration", хотя они широко приняты и используются, на самом деле не очень полезны, а иногда являются противоположностью тому, что в этом хранилище. рекомендую.

Здесь я хочу сказать, что существуют основополагающие принципы организации кода, и, основываясь на этих принципах, соглашения Ruby on Rails имеют смысл (в основном) для сообщества Ruby on Rails. Тем не менее, просто бездумно придерживаясь этих соглашений, теряет смысл. Как только вы освоите основные принципы, ВСЕ ваши проекты будут хорошо организованы и понятны: сценарии оболочки, игры, мобильные приложения, корпоративные проекты, даже ваш домашний каталог.

Сообщество Rails хочет иметь возможность иметь одного разработчика Rails, переключающегося с одного приложения на другое, и быть знакомым с ним каждый раз. Это имеет смысл, если у вас 37 сигналов или Pivotal Labs, и имеет свои преимущества. В мире серверного JavaScript общий идеал - это просто куда больше дикого запада, и у нас действительно нет проблем с этим. Вот так мы катимся. Мы к этому привыкли. Даже в express.js это близкий родственник Синатры, а не Rails, и получение соглашений от Rails обычно ничего не помогает. Я бы даже сказал Принципы над Конвенцией над Конфигурацией .

Основные принципы и мотивы

  • Быть умственно управляемым
    • Мозг может иметь дело только с небольшим количеством связанных вещей и думать о них одновременно. Вот почему мы используем каталоги. Это помогает нам справляться со сложностью, сосредотачиваясь на маленьких порциях.
  • Быть подходящим по размеру
    • Не создавайте «Каталоги Mansion», где есть только один файл, один на три каталога вниз. Вы можете увидеть это в Ansible Best Practices, которая заставляет небольшие проекты создавать более 10 каталогов для хранения более 10 файлов, когда 1 каталог с 3 файлами будет гораздо более подходящим. Вы не едете на работу на автобусе (если вы не водитель автобуса, но даже если вы ездите на автобусе, AT не работает), поэтому не создавайте структуры файловой системы, которые не оправданы фактическими файлами внутри них. ,
  • Будь модульным, но прагматичным
    • В целом сообщество узлов предпочитает небольшие модули. Все, что может быть чисто отделено от вашего приложения целиком, должно быть извлечено в модуль для внутреннего использования или публично опубликовано на npm. Тем не менее, для приложений среднего размера, которые являются здесь областью, накладные расходы могут добавить утомление вашему рабочему процессу без соответствующей стоимости. Так что на тот момент, когда у вас есть некоторый код, который выровнен, но недостаточен для оправдания совершенно отдельного модуля npm, просто считайте его « прото-модулем », ожидая, что когда он преодолеет некоторый порог размера, он будет извлечен.
    • Некоторые люди , такие как @ hij1nx даже включать в app/node_modulesкаталог и есть package.jsonфайлы в прото-модулях каталогов , чтобы облегчить этот переход и действовать в качестве напоминания.
  • Быть легко найти код
    • Нашей целью является сборка или исправление ошибки, и наша цель состоит в том, чтобы у разработчика не было проблем с поиском исходных файлов.
    • Имена значимы и точны
    • Неправильный код полностью удален, не оставлен в бесхозном файле или просто закомментирован
  • Быть дружественным к поиску
    • весь первоисточник исходного кода находится в appкаталоге, поэтому вы можете cdзапустить find / grep / xargs / ag / ack / etc и не отвлекаться на сторонние совпадения
  • Используйте простые и понятные названия
    • Теперь для npm требуются имена в нижнем регистре. Я нахожу это главным образом ужасным, но я должен следовать за стадом, поэтому имена файлов должны использовать, kebab-caseхотя имя переменной для этого должно быть в JavaScript, camelCaseпотому что в JavaScript -это знак минус.
    • имя переменной соответствует базовому имени пути модуля, но с kebab-caseпреобразованным вcamelCase
  • Группировка по соединению, а не по функции
    • Это серьезный отход от Ruby On Rails конвенции app/views, app/controllers, app/modelsи т.д.
    • Функции добавляются в полный стек, поэтому я хочу сосредоточиться на полном стеке файлов, которые имеют отношение к моей функции. Когда я добавляю поле телефонного номера в модель пользователя, мне не важен какой-либо контроллер, кроме пользовательского контроллера, и меня не волнует какая-либо модель, кроме пользовательской модели.
    • Таким образом, вместо того, чтобы редактировать 6 файлов, каждый из которых находится в своем собственном каталоге, и игнорировать тонны других файлов в этих каталогах, этот репозиторий организован так, что все файлы, которые мне нужны для создания функции, размещаются в одном месте.
    • По своей природе MVC, пользовательский вид связан с пользовательским контроллером, который связан с пользовательской моделью. Поэтому, когда я изменяю модель пользователя, эти 3 файла часто меняются вместе, но контроллер сделок или контроллер клиента разъединяются и, следовательно, не участвуют. То же самое относится и к проектам не-MVC, как правило.
    • Разъединение в стиле MVC или MOVE с точки зрения того, какой код входит, какой модуль все еще поощряется, но распространение файлов MVC в одноуровневые каталоги просто раздражает.
    • Таким образом, каждый из моих файлов маршрутов имеет ту часть маршрутов, которой он владеет. routes.rbФайл в стиле rails удобен, если вы хотите получить обзор всех маршрутов в приложении, но при создании объектов и исправлении ошибок вам важны только маршруты, относящиеся к изменяемой части.
  • Хранить тесты рядом с кодом
    • Это всего лишь пример «группирования по связям», но я хотел конкретно это назвать. Я написал много проектов, в которых тесты живут в параллельной файловой системе, называемой «тестами», и теперь, когда я начал размещать свои тесты в том же каталоге, что и соответствующий им код, я больше никогда не вернусь. Это более модульное решение, с которым гораздо проще работать в текстовых редакторах, и оно устраняет большую часть бессмысленного пути "../../ ..". Если вы сомневаетесь, попробуйте несколько проектов и решите сами. Я не собираюсь ничего делать, чтобы убедить вас, что это лучше.
  • Уменьшите сквозную связь с событиями
    • Легко подумать: «Хорошо, всякий раз, когда создается новая сделка, я хочу отправить электронное письмо всем продавцам», а затем просто введите код для отправки этих электронных писем по маршруту, который создает сделки.
    • Однако эта связь в конечном итоге превратит ваше приложение в гигантский шарик грязи.
    • Вместо этого DealModel должен просто запустить событие «create» и совершенно не знать, что еще система может сделать в ответ на это.
    • Когда вы кодируете таким образом, становится гораздо более возможным вложить весь код, связанный с пользователем, app/usersпотому что нет повсюду гнезда связанной бизнес-логики, загрязняющего чистоту базы кода пользователя.
  • Поток кода отслеживается
    • Не делай волшебных вещей. Не загружайте файлы из магических каталогов в файловой системе. Не будь Rails. Приложение запускается с, app/server.js:1и вы можете видеть все, что оно загружает и выполняет, следуя коду.
    • Не делайте DSL для ваших маршрутов. Не делайте глупого метапрограммирования, когда это не требуется.
    • Если ваше приложение является настолько большим , что делает magicRESTRouter.route(somecontroller, {except: 'POST'})это большой победа для вас более 3 основных app.get, app.put, app.del, звонки, вы , вероятно , строительство монолитного приложения , которое является слишком большим , чтобы эффективно работать. Получите фантазию для БОЛЬШИХ выигрышей, а не для преобразования 3 простых линий в 1 сложную линию.
  • Используйте имена файлов в нижнем регистре

    • Этот формат позволяет избежать проблем чувствительности к регистру файловой системы на разных платформах.
    • npm запрещает использование заглавных букв в именах новых пакетов, и это хорошо работает

      особенности express.js

  • Не используйте app.configure. Это почти полностью бесполезно, и вам просто не нужно это. Это в большом количестве из-за бессмысленной коппасты.

  • ПОРЯДОК СРЕДНЕГО ОБЕСПЕЧЕНИЯ И МАРШРУТОВ ПО ЭКСПРЕССАМ !!!
    • Почти каждая проблема маршрутизации, которую я вижу в stackoverflow, является неупорядоченным экспресс-промежуточным ПО
    • В общем, вы хотите, чтобы ваши маршруты были разделены и не полагались на порядок
    • Не используйте app.useдля всего приложения, если вам действительно нужно это промежуточное ПО только для 2 маршрутов (я смотрю на вас, body-parser)
    • Убедитесь, что, когда все сказано и сделано, у вас точно такой заказ:
      1. Любое очень важное промежуточное программное обеспечение для всего приложения
      2. Все ваши маршруты и различные промежуточные программы маршрутов
      3. ТО обработчики ошибок
  • К сожалению, будучи вдохновленным синатрой, express.js в основном предполагает, что все ваши маршруты будут в пути, server.jsи будет ясно, как они упорядочены. Для приложений среднего размера разбить вещи на отдельные модули маршрутов - это хорошо, но оно представляет опасность неработающего промежуточного программного обеспечения

Трюк с символической ссылкой на приложение

Есть много подходов , изложенных и обсужденные на длину сообщества в большой сущности лучше местным требуют () пути для Node.js . Вскоре я могу предпочесть либо "просто иметь дело с большим количеством ../../../ ..", либо использовать модуль requireFrom. Тем не менее, в данный момент я использую трюк с символическими ссылками, описанный ниже.

Таким образом, один из способов избежать внутрипроектных требований с помощью раздражающих относительных путей, таких как require("../../../config")использование следующего трюка:

  • создайте символическую ссылку под node_modules для вашего приложения
    • cd node_modules && ln -nsf ../app
  • добавьте в Git только символическую ссылку node_modules / app , а не всю папку node_modules
    • git add -f node_modules / app
    • Да, у вас все еще должно быть "node_modules" в вашем .gitignoreфайле
    • Нет, вы не должны помещать "node_modules" в ваш репозиторий git. Некоторые люди порекомендуют вам сделать это. Они неверны.
  • Теперь вы можете требовать внутрипроектные модули, используя этот префикс
    • var config = require("app/config");
    • var DealModel = require("app/deals/deal-model");
  • По сути, это заставляет внутрипроектные требования работать очень похоже на требования для внешних модулей npm.
  • Извините, пользователи Windows, вам нужно придерживаться относительных путей родительского каталога.

конфигурация

Как правило, модули кода и классы ожидают optionsпередачи только базового объекта JavaScript . Только модуль app/server.jsдолжен загружаться app/config.js. Оттуда он может синтезировать небольшие optionsобъекты для настройки подсистем по мере необходимости, но соединение каждой подсистемы с большим глобальным модулем конфигурации, полным дополнительной информации, является плохой связью.

Попробуйте централизовать создание соединений с БД и передавать их в подсистемы, а не передавать параметры соединений и заставлять подсистемы сами устанавливать исходящие соединения.

NODE_ENV

Это еще одна заманчивая, но ужасная идея, перенесенная с Rails. В вашем приложении должно быть ровно 1 место, app/config.jsкоторое смотрит на NODE_ENVпеременную окружения. Все остальное должно принимать явную опцию в качестве аргумента конструктора класса или параметра конфигурации модуля.

Если в модуле электронной почты есть опция для доставки электронной почты (SMTP, вход в stdout, очередь и т. Д.), Он должен выбрать вариант, подобный этому, {deliver: 'stdout'}но он абсолютно не должен проверять NODE_ENV.

тесты

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

  • foo.js имеет код модуля "foo"
  • foo.tape.js имеет основанные на узлах тесты для foo и живет в том же каталоге
  • foo.btape.js может использоваться для тестов, которые необходимо выполнить в среде браузера

Я использую глобусы файловой системы и find . -name '*.tape.js'команду, чтобы получить доступ ко всем моим тестам по мере необходимости.

Как организовать код в каждом .jsфайле модуля

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

  1. Для открытия блока CommonJS требуются вызовы зависимостей состояний
  2. Основной блок кода из чистого JavaScript. Никакого загрязнения CommonJS здесь. Не ссылаться на экспорт, модуль или требовать.
  3. Закрытие блока CommonJS для настройки экспорта
Питер Лайонс
источник
1
Что я должен использовать вместо bodyParser, если у меня есть только несколько маршрутов, которые его используют?
Илан Фрумер
3
Я нашел то, что искал здесь: stackoverflow.com/questions/12418372/…
Илан Фрумер
1
@wlingke проверьте gist.github.com/branneman/8048520 для подробного обсуждения доступных подходов к этой проблеме.
Питер Лайонс
@peterLyons Спасибо, что поделились этим. Прочитав, думаю, я напишу скрипт запуска. Спасибо!
wlingke
2
что касается трюк с символической ссылкой приложения , есть
Хайко Корюн
157

ОБНОВЛЕНИЕ (2013-10-29) : Пожалуйста, ознакомьтесь с моим другим ответом, в котором есть популярный спрос на JavaScript вместо CoffeeScript, а также типовое репозиторий github и подробное описание README с моими последними рекомендациями по этой теме.

конфиг

То, что вы делаете, прекрасно. Мне нравится иметь свое собственное пространство имен конфигурации, настроенное в config.coffeeфайле верхнего уровня с вложенным пространством имен, подобным этому.

#Set the current environment to true in the env object
currentEnv = process.env.NODE_ENV or 'development'
exports.appName = "MyApp"
exports.env =
  production: false
  staging: false
  test: false
  development: false
exports.env[currentEnv] = true
exports.log =
  path: __dirname + "/var/log/app_#{currentEnv}.log"
exports.server =
  port: 9600
  #In staging and production, listen loopback. nginx listens on the network.
  ip: '127.0.0.1'
if currentEnv not in ['production', 'staging']
  exports.enableTests = true
  #Listen on all IPs in dev/test (for testing from other machines)
  exports.server.ip = '0.0.0.0'
exports.db =
  URL: "mongodb://localhost:27017/#{exports.appName.toLowerCase()}_#{currentEnv}"

Это удобно для редактирования системного администратора. Затем, когда мне нужно что-то, как информация о подключении к БД, это

require('./config').db.URL

Маршруты / Контроллеры

Мне нравится оставлять свои маршруты с контроллерами и организовывать их в app/controllersподкаталоге. Затем я могу загрузить их и позволить им добавлять любые маршруты, которые им нужны.

В моем app/server.coffeeфайле coffeescript я делаю:

[
  'api'
  'authorization'
  'authentication'
  'domains'
  'users'
  'stylesheets'
  'javascripts'
  'tests'
  'sales'
].map (controllerName) ->
  controller = require './controllers/' + controllerName
  controller.setup app

Итак, у меня есть файлы вроде:

app/controllers/api.coffee
app/controllers/authorization.coffee
app/controllers/authentication.coffee
app/controllers/domains.coffee

И, например, в моем контроллере доменов у меня есть такая setupфункция.

exports.setup = (app) ->
  controller = new exports.DomainController
  route = '/domains'
  app.post route, controller.create
  app.put route, api.needId
  app.delete route, api.needId
  route = '/domains/:id'
  app.put route, controller.loadDomain, controller.update
  app.del route, controller.loadDomain, exports.delete
  app.get route, controller.loadDomain, (req, res) ->
    res.sendJSON req.domain, status.OK

Взгляды

Ввод взглядов app/viewsстановится привычным местом. Я выкладываю это так.

app/views/layout.jade
app/views/about.jade
app/views/user/EditUser.jade
app/views/domain/EditDomain.jade

Статические файлы

Перейти в publicподкаталог.

Github / Semver / НМП

Поместите файл уценки README.md в свой корень git-репо для github.

Поместите файл package.json с семантическим номером версии в корень вашего git-репо для NPM.

Питер Лайонс
источник
1
Привет, Питер! Мне действительно нравится такой подход, за которым вы идете. Я работаю над созданием экспресс-проекта, и я действительно хотел бы сделать все правильно, чем просто взломать его и разобрать. Было бы замечательно, если бы у вас был пример репо на github и / или в блоге.
suVasH .....
4
В этом репо есть несколько шаблонов, которые вы можете использовать в качестве ссылки: github.com/focusaurus/peterlyons.com
Питер Лайонс,
75
Сценарий кофе делает это трудным для чтения: / Есть ли шанс получить ванильное редактирование JS? Спасибо
toasted_flakes
1
Спасибо за этот ответ. Я просто пытаюсь обдумать это. Как вы app.put route, api.needId
получаете
@PeterLyons: эй, чувак, я видел твой исходный код, но понятия не имею, как сделать режим сборки, я уже установил Goи включил binфайл в структуру. Как вы запускаете этот goфайл bin?
user2002495
51

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


конфиг

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

// Set the current environment to true in the env object
var currentEnv = process.env.NODE_ENV || 'development';
exports.appName = "MyApp";
exports.env = {
  production: false,
  staging: false,
  test: false,
  development: false
};  
exports.env[currentEnv] = true;
exports.log = {
  path: __dirname + "/var/log/app_#{currentEnv}.log"
};  
exports.server = {
  port: 9600,
  // In staging and production, listen loopback. nginx listens on the network.
  ip: '127.0.0.1'
};  
if (currentEnv != 'production' && currentEnv != 'staging') {
  exports.enableTests = true;
  // Listen on all IPs in dev/test (for testing from other machines)
  exports.server.ip = '0.0.0.0';
};
exports.db {
  URL: "mongodb://localhost:27017/#{exports.appName.toLowerCase()}_#{currentEnv}"
};

Это удобно для редактирования системного администратора. Затем, когда мне нужно что-то, как информация о подключении к БД, это

require('./config').db.URL

Маршруты / Контроллеры

Мне нравится оставлять свои маршруты с контроллерами и организовывать их в app/controllersподкаталоге. Затем я могу загрузить их и позволить им добавлять любые маршруты, которые им нужны.

В моем app/server.jsфайле JavaScript я делаю:

[
  'api',
  'authorization',
  'authentication',
  'domains',
  'users',
  'stylesheets',
  'javascripts',
  'tests',
  'sales'
].map(function(controllerName){
  var controller = require('./controllers/' + controllerName);
  controller.setup(app);
});

Итак, у меня есть файлы вроде:

app/controllers/api.js
app/controllers/authorization.js
app/controllers/authentication.js
app/controllers/domains.js

И, например, в моем контроллере доменов у меня есть такая setupфункция.

exports.setup = function(app) {
  var controller = new exports.DomainController();
  var route = '/domains';
  app.post(route, controller.create);
  app.put(route, api.needId);
  app.delete(route, api.needId);
  route = '/domains/:id';
  app.put(route, controller.loadDomain, controller.update);
  app.del(route, controller.loadDomain, function(req, res){
    res.sendJSON(req.domain, status.OK);
  });
}

Взгляды

Ввод взглядов app/viewsстановится привычным местом. Я выкладываю это так.

app/views/layout.jade
app/views/about.jade
app/views/user/EditUser.jade
app/views/domain/EditDomain.jade

Статические файлы

Пойти в publicподкаталог.

Github / Semver / НМП

Поместите файл уценки README.md в свой корень git-репо для github.

Поместите файл package.json с семантическим номером версии в корень вашего git-репо для NPM.

dthree
источник
43

Мой вопрос был введен в апреле 2011 года, он тихий старый. За это время я смог улучшить свой опыт работы с Express.js и узнать, как создавать приложения, написанные с использованием этой библиотеки. Итак, я делюсь здесь своим опытом.

Вот моя структура каталогов:

├── app.js   // main entry
├── config   // The configuration of my applications (logger, global config, ...)
├── models   // The model data (e.g. Mongoose model)
├── public   // The public directory (client-side code)
├── routes   // The route definitions and implementations
├── services // The standalone services (Database service, Email service, ...)
└── views    // The view rendered by the server to the client (e.g. Jade, EJS, ...)

App.js

Цель app.jsфайла - запустить приложение expressjs. Он загружает модуль конфигурации, модуль логгера, ожидает подключения к базе данных, ... и запускает экспресс-сервер.

'use strict';
require('./config');
var database = require('./services/database');
var express = require('express');
var app = express();
module.exports = app;

function main() {
  var http = require('http');

  // Configure the application.
  app.configure(function () {
    // ... ... ...
  });
  app.configure('production', function () {
    // ... ... ...
  });
  app.configure('development', function () {
    // ... ... ...
  });

  var server = http.createServer(app);

  // Load all routes.
  require('./routes')(app);

  // Listen on http port.
  server.listen(3000);
}

database.connect(function (err) {
  if (err) { 
    // ...
  }
  main();
});

маршруты /

В каталоге маршрутов есть index.jsфайл. Его цель - ввести магию для загрузки всех других файлов внутри routes/каталога. Вот реализация:

/**
 * This module loads dynamically all routes modules located in the routes/
 * directory.
 */
'use strict';
var fs = require('fs');
var path = require('path');

module.exports = function (app) {
  fs.readdirSync('./routes').forEach(function (file) {
    // Avoid to read this current file.
    if (file === path.basename(__filename)) { return; }

    // Load the route file.
    require('./' + file)(app);
  });
};

С этим модулем создание нового определения и реализации маршрута действительно легко. Например hello.js:

function hello(req, res) {
  res.send('Hello world');
}

module.exports = function (app) {
  app.get('/api/hello_world', hello);
};

Каждый модуль маршрута является автономным .

Сандро Мунда
источник
Вы используете генератор для создания этой структуры?
Ашиш
18

Мне нравится использовать глобальное «приложение», а не экспортировать функцию и т. Д.

tjholowaychuk
источник
Я предпочитаю получать советы от создателей :) Кстати, не могли бы вы предоставить нам код?
воплощение
вот так. в этих приложениях вы видите - github.com/visionmedia/screenshot-app
diproart
17

Я думаю, что это отличный способ сделать это. Не ограничивается выражением, но я видел довольно много проектов node.js на github, делающих одно и то же. Они извлекают параметры конфигурации + меньшие модули (в некоторых случаях каждый URI) располагаются в отдельных файлах.

Я бы порекомендовал пройтись по специальным проектам на github, чтобы получить представление. ИМО, как вы делаете это правильно.

neebz
источник
16

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

Не делайте один большой MVC, но разделяйте его на модули

Так...

Почему?

  • Обычно один работает с одним модулем (например, с продуктами), который вы можете изменить самостоятельно.

  • Вы можете повторно использовать модули

  • Вы можете проверить это отдельно

  • Вы можете заменить его отдельно

  • У них есть четкие (стабильные) интерфейсы

    -Последнее, если работало несколько разработчиков, разделение модулей помогает

Проект nodebootstrap похож на мою окончательную структуру. ( GitHub )

Как выглядит эта структура?

  1. Маленькие капсулированные модули , каждый с отдельным MVC

  2. Каждый модуль имеет package.json

  3. Тестирование как часть структуры (в каждом модуле)

  4. Глобальная конфигурация , библиотеки и сервисы

  5. Интегрированный докер, кластер, навсегда

Folderoverview (см. Папку lib для модулей):

структура nodebootstrap

Саймон Факир
источник
3
Было бы полезно, если бы вы могли обновить обзорное изображение папки, а также отдельные модули, как пример того, как вы их структурируете.
youngrrrr
8

Я даю структуру папок в стиле MVC, пожалуйста, найдите ниже.

Мы использовали нижнюю структуру папок для наших больших и средних веб-приложений.

 myapp   
|
|
|____app
|      |____controllers
|      |    |____home.js
|      |
|      |____models
|      |     |___home.js
|      |
|      |____views
|           |___404.ejs
|           |___error.ejs
|           |___index.ejs
|           |___login.ejs
|           |___signup.ejs
|   
|
|_____config
|     |___auth.js
|     |___constants.js
|     |___database.js
|     |___passport.js
|     |___routes.js
|
|
|____lib
|    |___email.js
|
|____node_modules
|
|
|____public.js
|    |____css
|    |    |__style.css
|    |    
|    |____js
|    |    |__script.js
|    |
|    |____img
|    |    |__img.jpg
|    |
|    |
|    |____uploads
|         |__img.jpg
|      
|   
|
|_____app.js
|
|
|
|_____package.json

Я создал один модуль npm для генерации экспресс-структуры папок mvc.

Пожалуйста, найдите ниже https://www.npmjs.com/package/express-mvc-generator

Просто простые шаги для генерации и использования этих модулей.

я) установить модуль npm install express-mvc-generator -g

II) проверить параметры express -h

iii) Создать экспресс-структуру MVC express myapp

iv) Установить зависимости npm install::

v) Откройте ваш config / database.js, пожалуйста, настройте вашу базу данных mongo.

vi) Запустите приложение node appилиnodemon app

vii) Проверьте URL-адрес http: // localhost: 8042 / регистрация или http: // yourip: 8042 / регистрация

Раджа Рама Мохан Тавалам
источник
7

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

Ниже приведен длинный актуальный пост в блоге о лучших методах структурирования вашего приложения Express. http://www.terlici.com/2014/08/25/best-practices-express-structure.html

Существует также репозиторий GitHub, в котором применяются рекомендации из этой статьи. Он всегда в курсе последних версий Express.
https://github.com/terlici/base-express

Стефан
источник
7

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

application/
| - app.js
| - config.js
| - public/ (assets - js, css, images)
| - views/ (all your views files)
| - libraries/ (you can also call it modules/ or routes/)
    | - users.js
    | - products.js
    | - etc...

Таким образом, products.js и users.js будут содержать все ваши маршруты, и вся логика будет внутри.

ecdeveloper
источник
6

Ну, я поместил мои маршруты в файл json, который я прочитал в начале, и в цикле for в app.js установил маршруты. В файле route.json указывается, какой вид следует вызвать, и ключ для значений, которые будут отправлены в маршрут.
Это работает для многих простых случаев, но мне пришлось вручную создавать некоторые маршруты для особых случаев.

TiansHUo
источник
6

Я написал пост именно по этому вопросу. Он в основном использует a, routeRegistrarкоторый перебирает файлы в папке, /controllersвызывающей его функцию init. Функция initпринимает appпеременную Express в качестве параметра, чтобы вы могли регистрировать маршруты по своему усмотрению.

var fs = require("fs");
var express = require("express");
var app = express();

var controllersFolderPath = __dirname + "/controllers/";
fs.readdirSync(controllersFolderPath).forEach(function(controllerName){
    if(controllerName.indexOf("Controller.js") !== -1){
        var controller = require(controllersFolderPath + controllerName);
        controller.init(app);
    }
});

app.listen(3000);
Ренато Гама
источник
5

Это может представлять интерес:

https://github.com/flatiron/nconf

Иерархическая конфигурация node.js с файлами, переменными среды, аргументами командной строки и объединением атомарных объектов.

Улисс V
источник
4

1) Ваша файловая система проекта Express может выглядеть так:

/ ...
/lib
/node_modules
/public
/views
      app.js
      config.json
      package.json

app.js - ваш глобальный контейнер приложений

2) Основной файл модуля (lib / mymodule / index.js):

var express = require('express');    
var app = module.exports = express();
// and load module dependencies ...  

// this place to set module settings
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');

// then do module staff    
app.get('/mymodule/route/',function(req,res){ res.send('module works!') });

3) Подключите модуль в главном app.js

...
var mymodule = require('mymodule');
app.use(mymodule);

4) Пример логики

lib/login
lib/db
lib/config
lib/users
lib/verify
lib/
   /api/ 
   ...
lib/
   /admin/
      /users/
      /settings/
      /groups/
...
  • Лучший для тестирования
  • Лучший по масштабу
  • Отдельно зависит от модуля
  • Группировка маршрута по функциональности (или модулям)

tj говорит / покажет на Vimeo интересную идею о том, как модулировать экспресс-приложение - модульные веб-приложения с Node.js и Express . Мощный и простой.

diproart
источник
4

http://locomobiljs.org/ предоставляет способ структурировать приложение, созданное с помощью Node.js и Express.

С веб-сайта:

«Locomotive - это веб-фреймворк для Node.js. Locomotive поддерживает шаблоны MVC, маршруты RESTful и соглашения о конфигурации, а также легко интегрируется с любой базой данных и механизмом шаблонов. Locomotive опирается на Express, сохраняя мощь и простоту, которую вы ожидаете ожидать с узла ".

Бен Мордью
источник
3

Недавно я принял модули как независимые мини-приложения.

|-- src
  |--module1
  |--module2
     |--www
       |--img
       |--js
       |--css
     |--#.js
     |--index.ejs
  |--module3
  |--www
     |--bower_components
     |--img
     |--js
     |--css
  |--#.js
  |--header.ejs
  |--index.ejs
  |--footer.ejs

Теперь для любого модуля маршрутизации (# .js) представления (* .ejs), js, css и assets находятся рядом друг с другом. подмодульная маршрутизация настроена в родительском # .js с двумя дополнительными строками

router.use('/module2', opt_middleware_check, require('./module2/#'));
router.use(express.static(path.join(__dirname, 'www')));

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

Не забудьте установить вид на каталог src

app.set('views', path.join(__dirname, 'src'));
ЗЕВЕРО
источник
любая ссылка на github с такой структурой, заинтересованная в том, чтобы видеть, как загружаются маршруты, представления и модели
Мухаммед Умер
Я думаю, что все объясняется. Маршруты - это просто классические экспресс-маршруты. Виды должны быть загружены с префиксом с именами модулей, модели должны быть загружены путем ссылки на относительный путь.
Zevero
В моей последней строке я установил представление для каталога src. Таким образом, с этого момента все представления доступны относительно каталога src. Ничего фантастического.
Zevero
1

Так выглядит большая часть структуры моего экспресс-проекта.

Я обычно делаю express dirnameинициализацию проекта, прости мою лень, но он очень гибкий и расширяемый. PS - вам нужно получить express-generatorза это (для тех, кто ищет его sudo npm install -g express-generator, sudo, потому что вы устанавливаете его глобально)

|-- bin
    |-- www //what we start with "forever"
|-- bower_components
|-- models
    |-- database.js
    |-- model1.js //not this exact name ofcourse.
    |-- .
|-- node_modules
|-- public
    |-- images
    |-- javascripts
        |-- controllers
        |-- directives
        |-- services
        |-- app.js
        |-- init.js //contains config and used for initializing everything, I work with angular a lot.
    |-- stylesheets
|-- routes
    |-- some
    |-- hierarchy
    .
    .
|-- views
    |-- partials
    |-- content
|-- .env
|-- .env.template
|-- app.js
|-- README.md

Вам должно быть интересно, почему .env файлы? Потому что они работают! Я использую dotenvмодуль в своих проектах (много недавно), и он работает! Вставьте эти 2 заявления в app.jsилиwww

var dotenv = require('dotenv');
dotenv.config({path: path.join(__dirname + "/.env")});

И еще одна строка, чтобы быстро установить /bower_componentsдля обслуживания статического контента под ресурсом/ext

app.use('/ext', express.static(path.join(__dirname, 'bower_components')));

Возможно, он подойдет людям, которые хотят использовать Express и Angular вместе, или, javascriptsконечно , просто выразить без этой иерархии.

Нитеш Осваль
источник
1

Моя структура экспресс 4. https://github.com/odirleiborgert/borgert-express-boilerplate

пакеты

View engine: twig
Security: helmet
Flash: express-flash
Session: express-session
Encrypt: bcryptjs
Modules: express-load
Database: MongoDB
    ORM: Mongoose
    Mongoose Paginate
    Mongoose Validator
Logs: winston + winston-daily-rotate-file
Nodemon
CSS: stylus
Eslint + Husky

Структура

|-- app
    |-- controllers
    |-- helpers
    |-- middlewares
    |-- models
    |-- routes
    |-- services
|-- bin
|-- logs
|-- node_modules
|-- public
    |-- components
    |-- images
    |-- javascripts
    |-- stylesheets
|-- views
|-- .env
|-- .env-example
|-- app.js
|-- README.md
Одирлей Боргерт
источник
0

Простой способ структурировать приложение ur express:

  • В main index.js должен поддерживаться следующий порядок.

    все app.set должно быть первым.

    все app.use должно быть вторым.

    сопровождаемый другими apis с их функциями или route-continue в других файлах

    Exapmle

    app.use ("/ пароль", passwordApi);

    app.use ("/ user", userApi);

    app.post ("/ token", passport.createToken);

    app.post ("/ logout", passport.logout)

Snivio
источник
0

Лучший путь к структуре MVC для проекта ExpressJs с рулем и Passportjs

- app
      -config 
        -passport-setup.js
      -controllers
      -middleware
      -models
      -routes
      -service
    -bin
      -www
      -configuration.js
      -passport.js
    -node_modules
    -views
     -handlebars page
    -env
    -.gitignore
    -package.json
    -package-lock.json
Манишкумар Бхавнани
источник
@ sandro-munda, пожалуйста, проверьте
Манишкумар Бхавнани