1. ホーム
  2. node.js

[解決済み] ExpressJS アプリケーションをどのように構成するか?

2022-03-17 15:20:33

質問

NodeJS の Web フレームワークである ExpressJS を使用しています。

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);

config/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());
    });
};

config/routes.js

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

私のコードはうまく動作し、ディレクトリの構造も美しいと思います。しかし、コードは適合させなければならず、良い/美しいとは言えないと思います。

私のディレクトリ構造を使用してコードを適合させるか、単に1つのファイル(app.js)を使用する方が良いですか?

アドバイスありがとうございました。

どのように解決するのですか?

OK、しばらく経っていますが、これは人気のある質問なので、私は先に進み、JavaScriptコードと、私が中規模express.jsアプリケーションをどのように構成したいのかについての長いREADMEを含むscaffolding githubレポジトリを作成しました。

フォーカソーラス/エクスプレス・コード・ストラクチャー は、このための最新のコードを含むレポです。プルリクエストを歓迎します。

スタックオーバーフローはリンクだけの回答を好まないので、READMEのスナップショットを掲載します。これは新しいプロジェクトで、今後も更新していくつもりですが、最終的にはgithubリポジトリがこの情報の最新の場所になります。


Expressのコード構成

このプロジェクトは、中規模なexpress.jsのWebアプリケーションをどのように構成するかの一例です。

最低でもexpress v4.14 2016年12月までの最新版

アプリケーションの規模は?

Webアプリケーションはすべて同じではありませんし、すべてのexpress.jsアプリケーションに適用すべき単一のコード構造というものはないと私は考えています。

アプリケーションの規模が小さければ、ここに例示したような深いディレクトリ構造は必要ないでしょう。シンプルに、ほんの一握りの .js ファイルをリポジトリのルートに置けば完了です。これで完了です。

アプリケーションが巨大になった場合、ある時点でそれを個別の npm パッケージに分割する必要があります。一般的に、node.jsのアプローチは、少なくともライブラリについては、多くの小さなパッケージを好むようで、それが意味を持ち始め、オーバーヘッドを正当化するようになると、いくつかのnpmパッケージを使ってアプリケーションを構築する必要があります。アプリケーションが成長し、コードの一部がアプリケーションの外で明らかに再利用可能になるか、または明確なサブシステムになると、それを独自のgitリポジトリに移動して、独立したnpmパッケージにします。

そこで このプロジェクトの焦点は、中規模のアプリケーションで実行可能な構造を説明することです。

全体的なアーキテクチャはどのようなものですか

Webアプリケーションの構築には、以下のような様々なアプローチがあります。

  • Ruby on RailsのようなサーバーサイドMVC
  • MongoDB/Express/Angular/Node(MEAN)のようなシングルページアプリケーション形式
  • いくつかのフォームを含む基本的なWebサイト
  • モデル/操作/ビュー/イベントのスタイル MVCは死んだ、MOVE ONの時だ
  • その他、多くの歴史的、時事的な情報があります。

これらはそれぞれ別のディレクトリ構造にうまく収まるようになっています。この例では、単なる足場であり、完全に動作するアプリではありませんが、次のような重要なアーキテクチャポイントを想定しています。

  • このサイトには、従来の静的ページ/テンプレートがいくつかあります。
  • サイトのアプリケーション部分はSingle Page Applicationスタイルで開発されています。
  • アプリケーションは、REST/JSONスタイルのAPIをブラウザに公開します。
  • このアプリは単純なビジネスドメインをモデル化しており、この場合はカーディーラーのアプリケーションです

そして、Ruby on Railsはどうでしょうか。

Ruby on Railsで具現化された多くの考え方や、彼らが採用した "Convention over Configuration" の判断は、広く受け入れられ使われているものの、実はあまり有用ではなく、時にはこのリポジトリが推奨するものとは逆の場合もあるというのがこのプロジェクト全体のテーマとなります。

ここで私が言いたいのは、コードの整理には根本的な原則があり、その原則に基づいてRuby on Railsの規約はRuby on Railsコミュニティにとって(ほとんど)理にかなっている、ということです。しかし、ただ無思慮にこれらの規約を採用しても、要点がずれてしまいます。基本原則を理解すれば、シェルスクリプト、ゲーム、モバイルアプリ、エンタープライズプロジェクト、ホームディレクトリまで、すべてのプロジェクトがうまく整理され、明快になります。

Railsコミュニティにとって、一人のRails開発者がアプリからアプリに乗り換えても、そのたびに慣れ親しみ、快適に使えるようにしたいのでしょう。37 signalsやPivotal Labsであれば、これは非常に理にかなっており、メリットもあります。サーバーサイドのJavaScriptの世界では、全体的に「何でもあり」の精神が強く、私たちはそれを問題とはしていません。それが私たちのやり方です。私たちはそれに慣れているんです。express.jsはRailsではなくSinatraの近縁であり、Railsから慣習を取り入れることは通常、何の役にも立たないのです。私はこうさえ言います。 構成より原則、慣習より原理 .

基本理念と動機

  • 精神的に管理可能であること
    • 脳が一度に処理し、考えることができるのは、少数の関連した事柄だけです。そのため、ディレクトリを使うのです。小さな部分に集中することで、複雑さに対処することができるのです。
  • サイズに合ったものであること
    • 3つ下のディレクトリにたった1つのファイルがあるだけのquot;Mansion Directories"を作らないようにしましょう。この現象は Ansibleのベストプラクティス 小規模なプロジェクトでは、1つのディレクトリに3つのファイルを格納する方がはるかに適切なのに、10以上のファイルを格納するために10以上のディレクトリを作成するように辱めるのです。バスで通勤しているわけではないのですから(バスの運転手なら別ですが、その場合でもバスで通勤しているわけではありません)、中の実際のファイルによって正当化されないファイルシステム構造を作らないようにしましょう。
  • モジュラーでありながら実用的であること
    • nodeコミュニティでは、全体的に小さなモジュールが好まれます。アプリからきれいに分離できるものはすべて、内部で使用するか、npmで一般に公開するために、モジュールに抽出されるべきです。しかし、ここで対象としている中規模のアプリケーションでは、このオーバーヘッドが、それに見合う価値なしにワークフローに退屈さを加える可能性があります。そこで、ファクトアウトされたコードがあっても、完全に別のnpmモジュールを作るほどではない場合、それを" プロトモジュール ある程度の大きさの閾値を超えたら、抽出されることを想定しています。
    • のような人たちもいます。 hij1nx は、さらに app/node_modules ディレクトリを作成し package.json のファイルを プロトモジュール ディレクトリを使用することで、移行を容易にし、注意喚起の役割を果たします。
  • コードを見つけやすくする
    • 作るべき機能、直すべきバグがあるとき、開発者が関連するソースファイルを探すのに苦労しないことが私たちの目標です。
    • 名前に意味があり、正確であること
    • 残骸のようなコードは完全に削除され、孤立したファイルに残されたり、コメントアウトされただけではありません。
  • 検索に強い
    • すべてのファーストパーティソースコードは app ディレクトリの中にあるので cd があり、find/grep/xargs/ag/ack/等を実行すれば、第三者のマッチングに惑わされずに済みます。
  • シンプルでわかりやすいネーミングを使用する
    • npmは現在、パッケージ名をすべて小文字にすることを要求しているようです。私はこれはほとんどひどいと思いますが、群れに従わなければなりません。 kebab-case という変数名でなければならないのに、JavaScriptでは camelCase というのも - はJavaScriptではマイナス記号です。
    • 変数名はモジュールパスの basename と一致しますが、その後に kebab-case に変換されます。 camelCase
  • 機能別ではなく、結合別にグループ化する
    • これは、Ruby on Railsの慣例である app/views , app/controllers , app/models など
    • 機能はフルスタックに追加されるので、自分の機能に関連するファイルのフルスタックに焦点を当てたいのです。電話番号フィールドをユーザーモデルに追加するとき、私はユーザーコントローラ以外のコントローラは気にしませんし、ユーザーモデル以外のモデルも気にしません。
    • つまり、それぞれのディレクトリにある6つのファイルを編集し、それらのディレクトリにある他の大量のファイルを無視する代わりに、このリポジトリは、機能を構築するために必要なすべてのファイルが配置されるように構成されています。
    • MVCの性質上、ユーザービューはユーザコントローラと結合しており、コントローラはユーザモデルと結合しています。そのため、ユーザーモデルを変更すると、これら3つのファイルは一緒に変更されることが多いのですが、ディールコントローラーやカスタマーコントローラーは切り離されているので、関係ありません。これは、通常、非MVCデザインにも当てはまります。
    • どのコードをどのモジュールに入れるかという点で、MVCやMOVEスタイルのデカップリングはまだ奨励されていますが、MVCファイルを兄弟ディレクトリに分散させるのは迷惑なだけです。
    • このように、私のルートファイルには、それぞれ自分が所有するルートの部分があります。レイルスタイルの routes.rb ファイルは、アプリ内のすべてのルートの概要を把握したい場合に便利ですが、実際に機能を構築したりバグを修正したりする場合は、変更する部分に関連するルートだけを気にすることになります。
  • コードの横にテストを格納する
    • これは単なる結合によるグループ化の一例ですが、特に呼び出したかったのです。私は多くのプロジェクトを書いてきましたが、テストは "tests" と呼ばれる並列ファイルシステムの下に置かれていました。これはよりモジュール化され、テキストエディタで作業するのがはるかに簡単で、"../../..." パスのナンセンスを軽減します。もし迷うようなことがあれば、いくつかのプロジェクトで試してみて、自分で判断してください。私は、それがより良いものであることを納得させるために、これ以上のことをするつもりはありません。
  • イベントによる横断的な結合の削減
    • 新しいDealが作成されるたびに、すべてのSalespeopleにメールを送りたいと考えるのは簡単です。
    • しかし、このカップリングでは、最終的にアプリが巨大な泥の塊になってしまいます。
    • その代わりに、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 . ほとんど役に立たないし、必要ないだけです。心ないコピーパスタのために、多くの定型文に含まれています。

  • expressではミドルウェアとルートの順番が重要です!!!
    • stackoverflow で見るルーティングの問題のほとんどは、express のミドルウェアが順番通りになっていないことに起因しています。
    • 一般的に、ルートは分離して、順序にあまり依存しないようにしたいものです。
    • を使用しないでください。 app.use をアプリケーション全体に使用する場合、そのミドルウェアが本当に必要なのは2つのルートだけです (I'm looking at you, body-parser )
    • この順番を厳守してください。
      1. アプリケーション全体に関わる超重要なミドルウェアがある場合
      2. すべてのルートと各種ルートミドルウェア
      3. THENエラーハンドラ
  • 悲しいことに、sinatraに触発されたexpress.jsは、ほとんどの場合、すべてのルートが server.js そして、それらがどのように順序付けられるかが明確であることです。中規模なアプリケーションの場合、物事を個別のルートモジュールに分割するのは良いことですが、順番がバラバラになるミドルウェアの危険性があります。

アプリのシンボリックリンクのトリック

多くのアプローチがあり、コミュニティによって素晴らしい gist で詳細に説明されています。 Node.jsのためのより良いローカルrequire()パス . 私は近いうちに、たくさんの ../../../..." を扱うか、あるいは、.../.../..." を使用することを好むようになるかもしれません。 requireFrom modlueです。しかし、今のところ、私は以下に詳述するシンボリックリンクのトリックを使用しています。

というような相対パスがプロジェクト内で必要になるのを避けるための一つの方法です。 require("../../../config") は、次のようなトリックを使うことです。

  • node_modules の下にアプリ用のシンボリックリンクを作成します。
    • cd node_modules && ln -nsf ../app
  • 追加 node_modules/app シンボリックリンクだけです。 node_modules フォルダ全体ではなく、git に登録します。
    • git add -f node_modules/app
    • そうです。 .gitignore ファイル
    • いいえ、あなたは "node_modules" を git リポジトリに置くべきではありません。これを推奨する人もいます。彼らは間違っています。
  • このプレフィックスを使用して、プロジェクト内のモジュールを要求できるようになりました。
    • var config = require("app/config");
    • var DealModel = require("app/deals/deal-model") ;
  • 基本的には、プロジェクト内のrequireは、外部のnpmモジュールに対するrequireと非常によく似た動作をするようになります。
  • Windowsユーザの皆さん、申し訳ありませんが、親ディレクトリの相対パスにこだわってください。

設定方法

一般的に、モジュールやクラスは基本的なJavaScriptのみを想定してコーディングします。 options オブジェクトが渡されます。ただ app/server.js を読み込む必要があります。 app/config.js モジュールを使用します。そこから、小さな options しかし、すべてのサブシステムを余分な情報満載の大きなグローバル設定モジュールに結合するのは、悪い結合です。

接続パラメータを渡してサブシステムが自ら接続を行うのではなく、DB接続の作成を一元化し、サブシステムに渡すようにする。

ノードエンブ

これも魅力的ではありますが、Railsから引き継がれた恐ろしいアイデアです。アプリの中で正確に1箇所だけ存在するはずです。 app/config.js を見るのは NODE_ENV 環境変数を使用します。それ以外のものは、クラスのコンストラクタの引数やモジュールの設定パラメータとして、明示的にオプションを指定する必要があります。

email モジュールにメールを配信する方法 (SMTP, stdout へのログ出力, キューに入れるなど) のオプションがある場合、以下のようなオプションを取る必要があります。 {deliver: 'stdout'} をチェックすべきではありませんが、絶対に NODE_ENV .

テスト

私は、テストファイルを対応するコードと同じディレクトリに置き、ファイル名の拡張子の命名規則を使って、テストとプロダクションコードを区別しています。

  • foo.js は、モジュール "foo" のコードを持っています。
  • foo.tape.js は foo のノードベースのテストを持ち、同じディレクトリに住んでいます。
  • foo.btape.js ブラウザ環境での実行が必要なテストに使用できます。

ファイルシステムのグロブを使用し find . -name '*.tape.js' コマンドを使用して、必要に応じてすべてのテストにアクセスできるようにしました。

の中でどのようにコードを整理するか? .js モジュールファイル

このプロジェクトのスコープは、ファイルやディレクトリの行き先に関するものがほとんどで、それ以外のスコープはあまり加えたくありませんが、私はコードを3つの明確なセクションに整理していることだけはお伝えしておきます。

  1. CommonJS require 呼び出しのブロックが始まり、依存関係が示される
  2. 純粋なJavaScriptのメインコードブロック。ここにはCommonJSの汚染はありません。exportsやmodule、requireを参照しないようにしましょう。
  3. エクスポートを設定するCommonJSの〆ブロック