Гаразд, минув час, і це популярне питання, тому я пішов вперед і створив сховище лісів github з кодом JavaScript та довгим README про те, як мені подобається структурувати середній розмір програми express.js.
focusaurus / express_code_structure є репо з останнім кодом для цього. Прохання просити ласкаво просимо.
Ось короткий знімок README, оскільки stackoverflow не любить відповіді просто на посилання. Я проведу кілька оновлень, оскільки це новий проект, який я продовжуватиму оновлювати, але, зрештою, github repo стане актуальним місцем для цієї інформації.
Структура експрес-коду
Цей проект є прикладом того, як організувати веб-додаток express.js середнього розміру.
Поточний принаймні експрес v4.14 грудня 2016 року
Наскільки велика ваша заявка?
Веб-додатки не однакові, і, на мою думку, немає єдиної структури коду, яка повинна застосовуватися до всіх програм express.js.
Якщо ваша програма невелика, вам не потрібна така глибока структура каталогу, як показано тут. Просто тримайте це просто і вставте кілька .js
кореневих файлів у корінь вашого сховища, і ви закінчите. Voilà.
Якщо ваша програма величезна, вам в якийсь момент вам потрібно розбити її на різні npm-пакети. Загалом підхід node.js, здається, надає перевагу багатьом невеликим пакетам, принаймні, для бібліотек, і ви повинні створити свою програму, використовуючи кілька пакетів npm, оскільки це починає мати сенс і виправдовувати накладні витрати. Оскільки ваш додаток зростає і деяка частина коду стає чітко використаною поза вашою програмою або є чіткою підсистемою, перемістіть її у власне сховище git та перетворіть її в окремий пакет npm.
Тому основна мета цього проекту - проілюструвати працездатну структуру для додатків середнього розміру.
Яка ваша загальна архітектура
Існує багато підходів до створення веб-додатків, таких як
- Сторона сервера MVC a la Ruby on Rails
- Стиль застосування на одній сторінці a la MongoDB / Express / Angular / Node (MEAN)
- Основний веб-сайт з деякими формами
- Моделі / Операції / Перегляди / Стилі подій a la MVC мертвий, настав час ПОВЕРНУТИСЯ
- та багато інших як сучасних, так і історичних
Кожен з них добре вписується в іншу структуру каталогів. Для цього прикладу, це просто ліси і не повністю працює додаток, але я припускаю наступні ключові моменти архітектури:
- На сайті є деякі традиційні статичні сторінки / шаблони
- Частина сайту "додаток" розробляється як стиль застосування для однієї сторінки
- Додаток відкриває веб-переглядачу API стилю REST / JSON
- Додаток моделює простий бізнес-домен, в даному випадку це програма автосалону
А як щодо Ruby on Rails?
Темою цього проекту буде те, що багато ідей, втілених у рішеннях Ruby on Rails та «Конвенції про конфігурацію», які вони прийняли, хоча і широко приймаються та використовуються, насправді не є дуже корисними, а іноді є протилежними до того, що це сховище рекомендує.
Моя головна думка тут полягає в тому, що є основні принципи організації коду, і виходячи з цих принципів, конвенції Ruby on Rails мають сенс (переважно) для спільноти Ruby on Rails. Однак просто бездумне застосування цих конвенцій пропускає суть. Після того, як ви перейдете на основні принципи, ВСІ ваші проекти будуть добре організовані та зрозумілі: сценарії оболонки, ігри, мобільні додатки, корпоративні проекти, навіть домашній каталог.
Для спільноти Rails вони хочуть мати єдиний перемикач розробників Rails з програми на додаток до додатка, і щоразу вони будуть знайомі та комфортні. Це має великий сенс, якщо у вас 37 сигналів або основні лабораторії, і ви маєте переваги. У сервері JavaScript на стороні сервера загальний етос є просто набагато більш диким заходом, і ми з цим насправді не маємо проблем. Ось так ми котимось. Ми звикли до цього. Навіть у express.js це близька рідність Сінатри, а не Рейлів, і прийняття конвенцій з Рейлів зазвичай нічого не допомагає. Я б навіть сказав Принципи щодо Конвенції про конфігурацію .
Основні принципи та мотивації
- Бути розумовим керованим
- Мозок може розібратися і думати лише про невелику кількість пов’язаних речей одночасно. Ось чому ми використовуємо каталоги. Це допомагає нам впоратися зі складністю, зосереджуючись на невеликих порціях.
- Будьте відповідні розміру
- Не створюйте "Каталоги особняків", де є лише один файл, а по одному лише 3 каталоги. Ви можете бачити це в « Кращих практиках Ansible», які заважають невеликим проектам створювати 10+ каталогів для вміщення 10+ файлів, коли 1 каталог із 3 файлами буде набагато доречнішим. Ви не приводите автобус на роботу (якщо ви не водій автобуса, але навіть тоді, коли ви керуєте автобусом, АБО не працює), тому не створюйте структури файлової системи, які не виправдані фактичними файлами всередині них .
- Будьте модульними, але прагматичними
- Спільнота вузлів загалом надає перевагу невеликим модулям. Все, що можна повністю відокремити від вашої програми, має бути вилучено в модуль для внутрішнього використання або публічно опубліковано в npm. Однак для застосувань середнього розміру, які є сферою дії, накладні витрати цього можуть додавати тидію до вашого робочого процесу без відповідного значення. Тож на час, коли у вас є який-небудь код, який розглядають, але недостатньо, щоб виправдати повністю окремий npm-модуль, просто вважайте його " протомодулем " з розрахунком, що коли він переступить якийсь поріг розміру, він буде вилучений.
- Деякі люди , такі як @ hij1nx навіть включати в
app/node_modules
каталог і є package.json
файли в прото-модулях каталогів , щоб полегшити цей перехід і діяти в якості нагадування.
- Легко знайти код
- Враховуючи особливість збирання або помилку, яку потрібно виправити, наша мета полягає в тому, щоб у розробника не виникало труднощів за пошук вихідних файлів.
- Назви значущі та точні
- грубий код повністю видаляється, не залишається навколо сиротного файлу або просто коментується
- Будьте зручними для пошуку
- весь вихідний код стороннього виробника знаходиться в
app
каталозі, тому ви можете cd
запускати find / grep / xargs / ag / ack / тощо і не відволікатися на сторонні матчі
- Використовуйте просте і очевидне називання
- Тепер, схоже, 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
Файл у стилі рейки - це зручно, якщо ви хочете переглянути огляд усіх маршрутів у додатку, але, коли насправді вибудовуєте функції та виправляєте помилки, ви дбаєте лише про маршрути, що стосуються деталі, яку ви змінюєте.
- Зберігайте тести поруч із кодом
- Це лише екземпляр "групи за допомогою з'єднання", але я хотів конкретно назвати це. Я написав багато проектів, де тести живуть у паралельній файловій системі під назвою "тести", і тепер, коли я почав ставити свої тести в той самий каталог, що і їх відповідний код, я ніколи не повертаюся назад. Це більш модульно і набагато простіше працювати з текстовими редакторами та зменшує багато дурниць "../../ ..". Якщо ви сумніваєтесь, спробуйте це на кількох проектах та вирішіть самі. Я не збираюся робити нічого, крім цього, щоб переконати вас, що це краще.
- Зменшіть наскрізну зв’язок із подіями
- Неважко подумати "Гаразд, кожного разу, коли буде створено нову угоду, я хочу надіслати електронний лист усім продавцям", а потім просто покласти код, щоб відправити ці електронні листи в маршрут, який створює угоди.
- Однак ця зв'язок врешті-решт перетворить ваш додаток у гігантський кулька грязі.
- Натомість DealModel повинен просто запустити подію "створення" та бути абсолютно невідомим, що ще може зробити система у відповідь на це.
- Коли ви кодуєте таким чином, стає набагато більш можливим розмістити весь код, пов'язаний з користувачем,
app/users
оскільки всюди не існує гнізда зв'язаної бізнес-логіки щура, що забруднює чистоту бази коду користувача.
- Потік коду відстежується
- Не робіть чарівних речей. Не завантажуйте автоматично файли з чарівних каталогів у файлову систему. Не будьте Рейлами. Додаток запускається,
app/server.js:1
і ви можете побачити все, що він завантажує та виконує, дотримуючись коду.
- Не створюйте DSL для своїх маршрутів. Не робіть дурних метапрограмувань, коли це не вимагається.
- Якщо ваш додаток є настільки великим , що робить
magicRESTRouter.route(somecontroller, {except: 'POST'})
це великий перемога для вас більше 3 основних app.get
, app.put
, app.del
, дзвінки, ви , ймовірно , будівництво монолітного додатки , яке є занадто великим , щоб ефективно працювати. Отримайте фантазію на великі виграші, а не на перетворення 3 простих ліній в 1 складну лінію.
Використовуйте назви файлів із нижнього регістру шашлику
- Цей формат дозволяє уникнути проблем із чутливістю файлової системи до всіх платформ
- npm забороняє великі регістри в нових назвах пакетів, і це добре працює з цим
Специфікація express.js
Не використовуйте app.configure
. Це майже повністю марно, і вам просто це не потрібно. Це в безлічі котлів завдяки бездумним copypasta.
- ЗАМОВЛЕННЯ СЕРЕДНЬОГО ЗАБЕЗПЕЧЕННЯ ТА МАРШРУТУВАННЯ В ЕКСПРЕССІЙНИХ МАТЕРАХ !!!
- Майже кожна проблема маршрутизації, яку я бачу в stackoverflow, - це експрес-проміжне програмне забезпечення, яке не впорядковане
- Взагалі, ви хочете, щоб ваші маршрути були роз'єднані і не дуже покладалися на замовлення
- Не використовуйте
app.use
для всієї програми, якщо вам потрібно лише це програмне забезпечення для двох маршрутів (я дивлюся на вас, body-parser
)
- Переконайтесь, що коли все сказано і зроблено, ви точно отримали це замовлення:
- Будь-яке надзвичайно важливе проміжне програмне забезпечення для широкого застосування
- Всі ваші маршрути та різноманітні середні маршрути
- ТОГО оброблювачі помилок
- На жаль, будучи натхненним синатрами, express.js здебільшого передбачає, що всі ваші маршрути будуть наявними,
server.js
і буде зрозуміло, як вони впорядковані. Для додатків середнього розміру добре розбивати речі на окремі модулі маршрутів, але це вводить небезпеку середнього програмного забезпечення, яке не вийшло з ладу
Трюк додатка symlink
Є багато підходів , викладених і обговорені на довжину спільноти у великій суті краще місцевим вимагають () шляху для Node.js . Невдовзі я можу вирішити перевагу або "просто мати справу з безліччю ../../../ ..", або використовувати модуль requFrom. Однак на даний момент я використовую хитрість символьного посилання, детально описану нижче.
Отож, один із способів уникнути внутрішньопроектного проекту потребує таких дратівливих відносних шляхів, як require("../../../config")
:
- створити для своєї програми символьне посилання під node_modules
- cd node_modules && ln -nsf ../app
- додайте просто gode власне node_modules / додаток , а не всю папку node_modules до git
- git add -f node_modules / додаток
- Так, у вашому
.gitignore
файлі все ще залишиться "node_modules"
- Ні, ви не повинні ставити "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 різних розділи.
- Блок відкриття CommonJS вимагає викликів до залежностей від держави
- Основний блок коду pure-JavaScript. Тут немає забруднення CommonJS. Не посилайтеся на експорт, модуль та не вимагайте.
- Закриваючий блок CommonJS для налаштування експорту