OK, sudah lama dan ini adalah pertanyaan yang populer, jadi saya sudah maju dan membuat repositori github perancah dengan kode JavaScript dan README panjang tentang bagaimana saya ingin menyusun aplikasi express.js berukuran sedang.
focusaurus / express_code_structure adalah repo dengan kode terbaru untuk ini. Permintaan tarik diterima.
Ini snapshot README karena stackoverflow tidak menyukai jawaban hanya tautan. Saya akan membuat beberapa pembaruan karena ini adalah proyek baru yang akan saya terus perbarui, tetapi akhirnya repo github akan menjadi tempat terbaru untuk informasi ini.
Struktur Kode Ekspres
Proyek ini adalah contoh bagaimana mengatur aplikasi web express.js berukuran sedang.
Saat ini untuk setidaknya mengekspresikan v4.14 Desember 2016
Seberapa besar aplikasi Anda?
Aplikasi web tidak semuanya sama, dan menurut saya, tidak ada struktur kode tunggal yang harus diterapkan untuk semua aplikasi express.js.
Jika aplikasi Anda kecil, Anda tidak perlu struktur direktori yang dalam seperti yang dicontohkan di sini. Tetap sederhana dan tempelkan beberapa .js
file di root repositori Anda dan Anda selesai. Voa.
Jika aplikasi Anda besar, pada titik tertentu Anda perlu memecahnya menjadi paket npm yang berbeda. Secara umum pendekatan node.js tampaknya mendukung banyak paket kecil, setidaknya untuk pustaka, dan Anda harus membangun aplikasi Anda dengan menggunakan beberapa paket npm karena itu mulai masuk akal dan membenarkan overhead. Jadi ketika aplikasi Anda tumbuh dan sebagian kode menjadi jelas dapat digunakan kembali di luar aplikasi Anda atau merupakan subsistem yang jelas, pindahkan ke repositori git itu sendiri dan buatlah menjadi paket npm mandiri.
Jadi fokus dari proyek ini adalah untuk menggambarkan struktur yang bisa diterapkan untuk aplikasi berukuran sedang.
Apa arsitektur keseluruhan Anda
Ada banyak pendekatan untuk membangun aplikasi web, seperti
- Sisi Server MVC ala Ruby on Rails
- Gaya Aplikasi Halaman Tunggal ala MongoDB / Express / Angular / Node (MEAN)
- Situs web dasar dengan beberapa bentuk
- Model / Operasi / Tampilan / Acara gaya a MVC sudah mati, saatnya untuk PINDAHKAN
- dan banyak lainnya baik saat ini dan sejarah
Masing-masing cocok dengan baik ke dalam struktur direktori yang berbeda. Untuk keperluan contoh ini, ini hanya perancah dan bukan aplikasi yang berfungsi penuh, tapi saya mengasumsikan poin arsitektur utama berikut:
- Situs ini memiliki beberapa halaman / templat statis tradisional
- Bagian "aplikasi" dari situs ini dikembangkan sebagai gaya Aplikasi Halaman Tunggal
- Aplikasi menampilkan API gaya REST / JSON ke browser
- Aplikasi ini memodelkan domain bisnis sederhana, dalam hal ini, ini adalah aplikasi dealer mobil
Dan bagaimana dengan Ruby on Rails?
Ini akan menjadi tema di seluruh proyek ini bahwa banyak ide yang terkandung dalam Ruby on Rails dan keputusan "Konvensi Konfigurasi" yang telah mereka adopsi, meskipun diterima dan digunakan secara luas, sebenarnya tidak terlalu membantu dan kadang-kadang berlawanan dengan apa yang ada dalam repositori ini. merekomendasikan.
Poin utama saya di sini adalah bahwa ada prinsip-prinsip yang mendasari untuk mengatur kode, dan berdasarkan prinsip-prinsip itu, konvensi Ruby on Rails masuk akal (kebanyakan) untuk komunitas Ruby on Rails. Namun, hanya dengan menipu saja, konvensi-konvensi itu tidak tepat sasaran. Setelah Anda memahami prinsip-prinsip dasar, SEMUA proyek Anda akan terorganisir dengan baik dan jelas: skrip shell, game, aplikasi seluler, proyek perusahaan, bahkan direktori home Anda.
Untuk komunitas Rails, mereka ingin dapat memiliki satu pengembang Rails beralih dari satu aplikasi ke aplikasi dan menjadi akrab dan nyaman dengan itu setiap kali. Ini masuk akal jika Anda 37 sinyal atau Lab Penting, dan memiliki manfaat. Di dunia JavaScript sisi-server, etos keseluruhan hanyalah cara yang semakin liar dan kita tidak benar-benar memiliki masalah dengan itu. Begitulah cara kami menggulung. Kami sudah terbiasa. Bahkan di dalam express.js, ia adalah keluarga dekat Sinatra, bukan Rails, dan mengambil konvensi dari Rails biasanya tidak membantu apa pun. Saya bahkan akan mengatakan Principles over Convention over Configuration .
Prinsip dan Motivasi yang Mendasari
- Dapat dikelola secara mental
- Otak hanya dapat menangani dan memikirkan sejumlah kecil hal-hal terkait sekaligus. Itu sebabnya kami menggunakan direktori. Ini membantu kita menangani kompleksitas dengan berfokus pada porsi kecil.
- Sesuai ukuran
- Jangan membuat "Mansion Directories" di mana hanya ada 1 file sendirian 3 direktori di bawah. Anda dapat melihat ini terjadi di Ansible Best Practices yang memalukan proyek kecil untuk membuat 10+ direktori untuk menampung 10+ file ketika 1 direktori dengan 3 file akan jauh lebih tepat. Anda tidak mengendarai bus ke kantor (kecuali jika Anda seorang sopir bus, tetapi bahkan saat itu Anda mengendarai bus di tempat kerja tidak UNTUK), jadi jangan membuat struktur sistem file yang tidak dibenarkan oleh file aktual di dalamnya .
- Bersifat modular tetapi pragmatis
- Komunitas simpul secara keseluruhan mendukung modul-modul kecil. Apa pun yang dapat dipisahkan sepenuhnya dari aplikasi Anda sepenuhnya harus diekstraksi ke dalam modul baik untuk penggunaan internal atau dipublikasikan secara publik pada npm. Namun, untuk aplikasi berukuran sedang yang merupakan ruang lingkup di sini, overhead ini dapat menambah kebosanan pada alur kerja Anda tanpa nilai yang sepadan. Jadi untuk saat Anda memiliki beberapa kode yang difaktorkan tetapi tidak cukup untuk membenarkan modul npm yang benar-benar terpisah, anggap saja sebagai " proto-module " dengan harapan bahwa ketika melintasi beberapa ambang batas ukuran, itu akan diekstraksi.
- Beberapa orang seperti @ hij1nx bahkan menyertakan
app/node_modules
direktori dan memiliki package.json
file dalam direktori proto-module untuk memfasilitasi transisi itu dan bertindak sebagai pengingat.
- Mudah menemukan kode
- Diberi fitur untuk membangun atau bug untuk memperbaikinya, tujuan kami adalah agar pengembang tidak kesulitan menemukan file sumber yang terlibat.
- Nama bermakna dan akurat
- kode crufty sepenuhnya dihapus, tidak ditinggalkan dalam file yatim atau hanya berkomentar
- Ramah pencarian
- semua kode sumber pihak pertama ada di
app
direktori sehingga Anda dapat cd
menjalankan run / grep / xargs / ag / ack / etc dan tidak terganggu oleh pertandingan pihak ketiga
- Gunakan penamaan yang sederhana dan jelas
- npm sekarang tampaknya membutuhkan semua nama paket huruf kecil. Saya menemukan ini sebagian besar mengerikan tetapi saya harus mengikuti kawanan, sehingga nama file harus menggunakan
kebab-case
meskipun nama variabel untuk itu dalam JavaScript harus camelCase
karena -
merupakan tanda minus dalam JavaScript.
- nama variabel cocok dengan nama path modul, tetapi dengan
kebab-case
ditransformasikan menjadicamelCase
- Kelompokkan dengan Kopling, Bukan berdasarkan Fungsi
- Ini adalah keberangkatan utama dari Ruby on Rails konvensi
app/views
, app/controllers
, app/models
, dll
- Fitur ditambahkan ke tumpukan penuh, jadi saya ingin fokus pada setumpuk penuh file yang relevan dengan fitur saya. Ketika saya menambahkan bidang nomor telepon ke model pengguna, saya tidak peduli tentang pengontrol selain pengontrol pengguna, dan saya tidak peduli tentang model apa pun selain model pengguna.
- Jadi, alih-alih mengedit 6 file yang masing-masing di direktori mereka sendiri dan mengabaikan banyak file lain di direktori tersebut, repositori ini diatur sedemikian rupa sehingga semua file yang saya butuhkan untuk membangun fitur dilokasi
- Berdasarkan sifat MVC, tampilan pengguna digabungkan ke pengontrol pengguna yang digabungkan ke model pengguna. Jadi ketika saya mengubah model pengguna, 3 file tersebut akan sering berubah bersama, tetapi pengontrol penawaran atau pengontrol pelanggan dipisahkan dan dengan demikian tidak terlibat. Hal yang sama berlaku untuk desain non-MVC biasanya juga.
- Gaya decoupling MVC atau MOVE dalam hal kode mana di mana modul masih didorong, tetapi menyebarkan file MVC ke direktori saudara hanya menjengkelkan.
- Jadi masing-masing file rute saya memiliki bagian dari rute yang dimilikinya.
routes.rb
File gaya rel berguna jika Anda menginginkan ikhtisar semua rute di aplikasi, tetapi ketika benar-benar membangun fitur dan memperbaiki bug, Anda hanya peduli dengan rute yang relevan dengan bagian yang Anda ubah.
- Simpan tes di sebelah kode
- Ini hanya contoh dari "grup dengan kopling", tapi saya ingin menyebutnya secara khusus. Saya telah menulis banyak proyek di mana tes hidup di bawah sistem file paralel yang disebut "tes" dan sekarang saya sudah mulai meletakkan tes saya di direktori yang sama dengan kode yang sesuai, saya tidak akan pernah kembali. Ini lebih modular dan lebih mudah untuk dikerjakan dengan editor teks dan mengurangi banyak jalan "../../ .." omong kosong. Jika Anda ragu, coba di beberapa proyek dan putuskan sendiri. Saya tidak akan melakukan apa pun di luar ini untuk meyakinkan Anda bahwa itu lebih baik.
- Kurangi sambungan silang dengan Acara
- Sangat mudah untuk berpikir "OK, setiap kali Kesepakatan baru dibuat, saya ingin mengirim email ke semua Tenaga Penjualan", dan kemudian hanya meletakkan kode untuk mengirim email itu di rute yang membuat kesepakatan.
- Namun, kopling ini pada akhirnya akan mengubah aplikasi Anda menjadi bola lumpur raksasa.
- Alih-alih, DealModel seharusnya hanya menjalankan acara "buat" dan sama sekali tidak mengetahui apa lagi yang mungkin dilakukan sistem dalam menanggapi hal itu.
- Ketika Anda membuat kode dengan cara ini, akan jauh lebih mungkin untuk memasukkan semua kode terkait pengguna ke dalam
app/users
karena tidak ada sarang tikus dari logika bisnis ditambah di semua tempat yang mencemari kemurnian basis kode pengguna.
- Aliran kode bisa diikuti
- Jangan lakukan hal-hal ajaib. Jangan memuat file secara otomatis dari direktori ajaib di sistem file. Jangan menjadi rel. Aplikasi dimulai
app/server.js:1
dan Anda dapat melihat semua yang dimuat dan dijalankan dengan mengikuti kode.
- Jangan membuat DSL untuk rute Anda. Jangan melakukan metaprogram konyol ketika tidak dipanggil.
- Jika aplikasi Anda begitu besar bahwa melakukan
magicRESTRouter.route(somecontroller, {except: 'POST'})
adalah kemenangan besar bagi Anda lebih dari 3 dasar app.get
, app.put
, app.del
, panggilan, Anda mungkin membangun aplikasi monolitik yang terlalu besar untuk secara efektif bekerja pada. Dapatkan mewah untuk kemenangan BESAR, bukan untuk mengonversi 3 baris sederhana menjadi 1 baris kompleks.
Gunakan nama file huruf kecil kebab
- Format ini menghindari masalah sensitivitas case sistem file di seluruh platform
- npm melarang huruf besar dalam nama paket baru, dan ini berfungsi baik dengan itu
spesifik express.js
Jangan gunakan app.configure
. Ini hampir seluruhnya tidak berguna dan Anda hanya tidak membutuhkannya. Ada banyak boilerplate karena copypasta yang tidak ada artinya.
- PESANAN MIDDLEWARE DAN ROUTES DALAM MASALAH EXPRESS !!!
- Hampir setiap masalah routing yang saya lihat di stackoverflow adalah middleware ekspres yang tidak sesuai pesanan
- Secara umum, Anda ingin rute Anda dipisahkan dan tidak terlalu bergantung pada pesanan
- Jangan gunakan
app.use
untuk seluruh aplikasi Anda jika Anda benar-benar hanya membutuhkan middleware untuk 2 rute (Saya melihat Anda, body-parser
)
- Pastikan ketika semua dikatakan dan dilakukan, Anda memiliki PERSIS pesanan ini:
- Semua middleware aplikasi super penting
- Semua rute Anda dan berbagai rute middlewares
- MAKA penangan kesalahan
- Sayangnya, karena terinspirasi oleh sinatra, express.js sebagian besar mengasumsikan semua rute Anda akan berada
server.js
dan akan jelas bagaimana mereka dipesan. Untuk aplikasi berukuran sedang, membagi hal-hal ke dalam modul rute terpisah itu bagus, tetapi hal itu memperkenalkan bahaya middleware rusak
Trik symlink aplikasi
Ada banyak pendekatan yang diuraikan dan dibahas panjang lebar oleh masyarakat dalam inti besar baik lokal memerlukan () jalur untuk Node.js . Saya mungkin segera memutuskan untuk memilih "hanya berurusan dengan banyak ../../../ .." atau menggunakan memerlukan dari mode. Namun, saat ini, saya telah menggunakan trik symlink yang dirinci di bawah ini.
Jadi salah satu cara untuk menghindari kebutuhan dalam proyek dengan jalur relatif yang mengganggu seperti require("../../../config")
adalah dengan menggunakan trik berikut:
- buat symlink di bawah node_modules untuk aplikasi Anda
- cd node_modules && ln -nsf ../app
- tambahkan saja node_modules / app symlink itu sendiri , bukan seluruh folder node_modules, ke git
- git tambahkan -f node_modules / app
- Ya, Anda masih harus memiliki "node_modules" di
.gitignore
file Anda
- Tidak, Anda tidak harus memasukkan "node_modules" ke repositori git Anda. Beberapa orang akan merekomendasikan Anda melakukan ini. Mereka salah.
- Sekarang Anda dapat memerlukan modul intra-proyek menggunakan awalan ini
var config = require("app/config");
var DealModel = require("app/deals/deal-model")
;
- Pada dasarnya, ini membuat intra-proyek membutuhkan kerja yang sangat mirip dengan yang dibutuhkan untuk modul npm eksternal.
- Maaf, pengguna Windows, Anda harus tetap menggunakan jalur relatif direktori induk.
Konfigurasi
Umumnya kode modul dan kelas untuk mengharapkan hanya options
objek JavaScript dasar yang diteruskan. Hanya app/server.js
harus memuat app/config.js
modul. Dari sana dapat mensintesis options
objek kecil untuk mengkonfigurasi subsistem yang diperlukan, tetapi menyambungkan setiap subsistem ke modul konfigurasi global besar yang penuh dengan informasi tambahan adalah kopling yang buruk.
Cobalah untuk memusatkan pembuatan koneksi DB dan mengirimkannya ke dalam subsistem yang bertentangan dengan melewati parameter koneksi dan membuat subsistem membuat koneksi keluar sendiri.
NODE_ENV
Ini adalah ide lain yang menarik tetapi mengerikan yang dibawa dari Rails. Seharusnya ada 1 tempat di aplikasi Anda, app/config.js
yang terlihat pada NODE_ENV
variabel lingkungan. Segala sesuatu yang lain harus mengambil opsi eksplisit sebagai argumen konstruktor kelas atau parameter konfigurasi modul.
Jika modul email memiliki opsi tentang cara mengirim email (SMTP, masuk ke stdout, masukkan antrian, dll), ia harus mengambil opsi seperti {deliver: 'stdout'}
tetapi sama sekali tidak perlu memeriksa NODE_ENV
.
Tes
Sekarang saya menyimpan file pengujian saya di direktori yang sama dengan kode yang sesuai dan menggunakan konvensi penamaan ekstensi nama file untuk membedakan tes dari kode produksi.
foo.js
memiliki kode "foo" modul
foo.tape.js
memiliki tes berbasis simpul untuk foo dan tinggal di dir yang sama
foo.btape.js
dapat digunakan untuk tes yang perlu dijalankan di lingkungan browser
Saya menggunakan gumpalan filesystem dan find . -name '*.tape.js'
perintah untuk mendapatkan akses ke semua pengujian saya sebagaimana diperlukan.
Cara mengatur kode dalam setiap .js
file modul
Cakupan proyek ini kebanyakan tentang ke mana file dan direktori pergi, dan saya tidak ingin menambahkan banyak ruang lingkup lain, tetapi saya hanya akan menyebutkan bahwa saya mengatur kode saya menjadi 3 bagian yang berbeda.
- Membuka blok CommonJS membutuhkan panggilan ke dependensi negara
- Blok kode utama murni-JavaScript. Tidak ada polusi CommonJS di sini. Jangan merujuk ekspor, modul, atau mengharuskan.
- Blok penutup CommonJS untuk mengatur ekspor