Temui Phoenix: Kerangka Seperti Rel untuk Aplikasi Web Modern di Elixir

Diterbitkan: 2022-03-11

Kerangka kerja Phoenix telah berkembang dengan popularitas dengan cepat, menawarkan produktivitas kerangka kerja seperti Ruby on Rails, sementara juga menjadi salah satu kerangka kerja tercepat yang tersedia. Ini mematahkan mitos bahwa Anda harus mengorbankan kinerja untuk meningkatkan produktivitas.

Jadi apa sebenarnya Phoenix itu?

Phoenix adalah kerangka kerja web yang dibangun dengan bahasa pemrograman Elixir. Elixir, dibangun di atas Erlang VM, digunakan untuk membangun sistem terdistribusi dengan latensi rendah, toleran terhadap kesalahan, yang semakin diperlukan kualitas aplikasi web modern. Anda dapat mempelajari lebih lanjut tentang Elixir dari posting blog ini atau panduan resmi mereka.

Jika Anda seorang pengembang Ruby on Rails, Anda pasti harus tertarik dengan Phoenix karena keuntungan kinerja yang dijanjikannya. Pengembang kerangka kerja lain juga dapat mengikuti untuk melihat bagaimana Phoenix mendekati pengembangan web.

Temui Phoenix di Elixir: Kerangka Seperti Rel untuk Aplikasi Web Modern

Pada artikel ini kita akan mempelajari beberapa hal di Phoenix yang harus Anda ingat jika Anda berasal dari dunia Ruby on Rails.

Tidak seperti Ruby, Elixir adalah bahasa pemrograman fungsional, yang mungkin merupakan perbedaan terbesar yang mungkin harus Anda tangani. Namun, ada beberapa perbedaan utama antara kedua platform ini yang harus diperhatikan oleh siapa pun yang mempelajari Phoenix untuk memaksimalkan produktivitas mereka.

Konvensi penamaan lebih sederhana.

Ini adalah yang kecil, tetapi mudah dikacaukan jika Anda berasal dari Ruby on Rails.

Di Phoenix, konvensinya adalah menulis semuanya dalam bentuk tunggal. Jadi Anda akan memiliki "UserController" alih-alih "UsersController" seperti yang Anda lakukan di Ruby on Rails. Ini berlaku di mana-mana kecuali saat memberi nama tabel database, di mana konvensinya adalah memberi nama tabel dalam bentuk jamak.

Ini mungkin tidak tampak seperti masalah besar setelah mengetahui kapan dan di mana menggunakan bentuk jamak di Ruby on Rails, tetapi awalnya agak membingungkan, ketika saya belajar menggunakan Ruby on Rails, dan saya yakin itu membingungkan banyak orang. orang lain juga. Phoenix memberi Anda satu hal lagi yang perlu dikhawatirkan.

Perutean lebih mudah dikelola.

Phoenix dan Ruby on Rails sangat mirip dalam hal perutean. Perbedaan utama adalah bagaimana Anda dapat mengontrol cara permintaan diproses.

Di Ruby on Rails (dan aplikasi Rack lainnya) ini dilakukan melalui middleware, sedangkan di Phoenix, ini dilakukan dengan apa yang disebut sebagai "colokan."

Colokan adalah apa yang Anda gunakan untuk memproses koneksi.

Misalnya, middleware Rails::Rack::Logger mencatat permintaan di Rails, yang di Phoenix akan dilakukan dengan plug Plug.Logger . Pada dasarnya apa pun yang dapat Anda lakukan di Rack middleware dapat dilakukan dengan menggunakan colokan.

Berikut adalah contoh router di Phoenix:

 defmodule HelloWorld.Router do use HelloWorld.Web, :router pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers end pipeline :api do plug :accepts, ["json"] end scope "/", HelloWorld do pipe_through :browser get "/", HelloController, :index end scope "/api", HelloWorld do pipe_through :api end end

Pertama, mari kita lihat jalur pipanya.

Ini adalah kelompok colokan di mana permintaan akan berjalan. Anggap saja sebagai tumpukan middleware. Ini dapat digunakan, misalnya, untuk memverifikasi bahwa permintaan mengharapkan HTML, mengambil sesi, dan memastikan permintaan aman. Ini semua terjadi sebelum mencapai controller.

Karena Anda dapat menentukan beberapa saluran pipa, Anda dapat memilih colokan mana yang diperlukan untuk rute tertentu. Di router kami, misalnya, kami memiliki saluran berbeda untuk permintaan halaman (HTML) dan API (JSON).

Tidak selalu masuk akal untuk menggunakan saluran yang sama persis untuk berbagai jenis permintaan. Phoenix memberi kita fleksibilitas itu.

Tampilan dan template adalah dua hal yang berbeda.

Tampilan di Phoenix tidak sama dengan tampilan di Ruby on Rails.

Tampilan di Phoenix bertanggung jawab untuk merender template dan menyediakan fungsi yang membuat data mentah lebih mudah digunakan oleh template. Tampilan di Phoenix paling mirip dengan helper di Ruby on Rails, dengan tambahan rendering template.

Mari kita tulis sebuah contoh yang menampilkan “Halo, Dunia!” di peramban.

Ruby di Rel:

app/controllers/hello_controller.rb:

 class HelloController < ApplicationController def index end end

app/views/hello/index.html.erb:

 <h1>Hello, World!</h1>

Phoenix:

web/controllers/hello_controller.ex:

 defmodule HelloWorld.HelloController do use HelloWorld.Web, :controller def index(conn, _params) do render conn, "index.html" end end

web/views/hello_view.ex:

 defmodule HelloWorld.HelloView do use HelloWorld.Web, :view end

web/templates/hello/index.html.eex:

 <h1>Hello, World!</h1>

Kedua contoh ini menampilkan "Halo, Dunia!" di browser tetapi memiliki beberapa perbedaan utama.

Pertama, Anda harus secara eksplisit menyatakan template yang ingin Anda render di Phoenix tidak seperti di Ruby on Rails.

Selanjutnya, kami harus menyertakan tampilan di Phoenix yang berdiri di antara controller dan template.

Sekarang, Anda mungkin bertanya-tanya mengapa kami membutuhkan tampilan jika kosong? Kuncinya di sini adalah, di bawah tenda, Phoenix mengkompilasi template menjadi fungsi yang kira-kira sama dengan kode di bawah ini:

 defmodule HelloWorld.HelloView do use HelloWorld.Web, :view def render("index.html", _assigns) do raw("<h1>Hello, World!</h1>") end end

Anda dapat menghapus template, dan menggunakan fungsi render baru, dan Anda akan mendapatkan hasil yang sama. Ini berguna ketika Anda hanya ingin mengembalikan teks atau bahkan JSON.

Model hanya itu: model data.

Di Phoenix, model terutama menangani validasi data, skemanya, hubungannya dengan model lain, dan cara penyajiannya.

Menentukan skema dalam model mungkin terdengar aneh pada awalnya, tetapi ini memungkinkan Anda untuk dengan mudah membuat bidang "virtual", yang merupakan bidang yang tidak disimpan ke database. Mari kita lihat sebuah contoh:

 defmodule HelloPhoenix.User do use HelloPhoenix.Web, :model schema "users" do field :name, :string field :email, :string field :password, :string, virtual: true field :password_hash, :string end end

Di sini kita mendefinisikan empat bidang dalam tabel "pengguna": nama, email, kata sandi, dan kata sandi_hash.

Tidak banyak yang menarik di sini, kecuali bidang "kata sandi" yang ditetapkan sebagai "virtual."

Ini memungkinkan kita untuk mengatur dan mendapatkan bidang ini tanpa menyimpan perubahan ke database. Ini berguna karena kita akan memiliki logika untuk mengubah kata sandi itu menjadi hash, yang akan kita simpan di bidang “password_hash” dan kemudian menyimpannya ke database.

Anda masih perlu membuat migrasi; skema dalam model diperlukan karena bidang tidak secara otomatis dimuat ke dalam model seperti di Ruby on Rails.

Perbedaan antara Phoenix dan Ruby on Rails adalah bahwa model tidak menangani persistensi ke database. Ini ditangani oleh modul yang disebut "Repo" yang dikonfigurasi dengan informasi database, seperti yang ditunjukkan pada contoh di bawah ini:

 config :my_app, Repo, adapter: Ecto.Adapters.Postgres, database: "ecto_simple", username: "postgres", password: "postgres", hostname: "localhost"

Kode ini disertakan dalam file konfigurasi khusus lingkungan, seperti config/dev.exs atau config/test.exs . Ini memungkinkan kita untuk kemudian menggunakan Repo untuk melakukan operasi basis data seperti membuat dan memperbarui.

 Repo.insert(%User{name: "John Smith", example: "[email protected]"}) do {:ok, user} -> # Insertion was successful {:error, changeset} -> # Insertion failed end

Ini adalah contoh umum pada pengontrol di Phoenix.

Kami memberikan nama dan email kepada Pengguna dan Repo mencoba membuat catatan baru ke dalam database. Kami kemudian dapat, secara opsional, menangani upaya yang berhasil atau gagal seperti yang ditunjukkan pada contoh.

Untuk memahami kode ini dengan lebih baik, Anda perlu memahami pencocokan pola di Elixir. Nilai yang dikembalikan oleh fungsi adalah tuple. Fungsi mengembalikan tuple dari dua nilai, status, dan kemudian model atau set perubahan. Changeset adalah cara untuk melacak perubahan dan memvalidasi model (changeset akan dibahas di bagian selanjutnya).

Tuple pertama, dari atas ke bawah, yang cocok dengan pola tupel yang dikembalikan oleh fungsi yang mencoba memasukkan Pengguna ke dalam database, akan menjalankan fungsi yang ditentukan.

Kita dapat menetapkan variabel untuk status alih-alih atom (yang pada dasarnya adalah simbol di Ruby), tetapi kemudian kita akan mencocokkan upaya yang berhasil dan yang gagal dan akan menggunakan fungsi yang sama untuk kedua situasi tersebut. Menentukan atom mana yang ingin kita cocokkan, kita mendefinisikan fungsi khusus untuk status itu.

Ruby on Rails memiliki fungsionalitas ketekunan yang dibangun ke dalam model melalui ActiveRecord. Ini menambah lebih banyak tanggung jawab pada model dan terkadang dapat membuat pengujian model menjadi lebih kompleks sebagai hasilnya. Di Phoenix, ini telah dipisahkan dengan cara yang masuk akal dan mencegah kembung setiap model dengan logika ketekunan.

Changesets memungkinkan validasi dan aturan transformasi yang jelas.

Di Ruby on Rails, memvalidasi dan mengubah data dapat menjadi sumber bug yang sulit ditemukan. Ini karena tidak segera terlihat saat data diubah oleh panggilan balik seperti "before_create" dan validasi.

Di Phoenix, Anda secara eksplisit melakukan validasi dan transformasi ini menggunakan set perubahan. Ini adalah salah satu fitur favorit saya di Phoenix.

Mari kita lihat set perubahan dengan menambahkan satu ke model sebelumnya:

 defmodule HelloPhoenix.User do use HelloPhoenix.Web, :model schema "users" do field :name, :string field :email, :string field :password, :string, virtual: true field :password_hash, :string end def changeset(struct, params \\ %{}) do struct |> cast(params, [:name, :email, :password]) |> validate_required([:email, :password]) end end

Di sini changeset melakukan dua hal.

Pertama, ia memanggil fungsi "cast" yang merupakan daftar putih bidang yang diizinkan, mirip dengan "strong_parameters" di Ruby on Rails, dan kemudian memvalidasi bahwa bidang "email" dan "kata sandi" disertakan, membuat bidang "nama" pilihan. Dengan cara ini, hanya bidang yang Anda izinkan yang dapat dimodifikasi oleh pengguna.

Hal yang menyenangkan tentang pendekatan ini adalah kita tidak terbatas pada satu set perubahan. Kita dapat membuat beberapa perubahan. Sebuah changeset untuk pendaftaran dan satu untuk memperbarui pengguna adalah hal biasa. Mungkin kita hanya ingin meminta kolom kata sandi saat mendaftar tetapi tidak saat memperbarui pengguna.

Bandingkan pendekatan ini dengan apa yang biasa dilakukan di Ruby on Rails, Anda harus menentukan bahwa validasi harus dijalankan hanya pada "buat". Ini terkadang menyulitkan Rails untuk mengetahui apa yang dilakukan kode Anda setelah Anda memiliki model yang kompleks.

Mengimpor fungsionalitas sangat mudah, namun fleksibel.

Sebagian besar fungsi perpustakaan yang disebut "Ecto" diimpor ke dalam model. Model biasanya memiliki garis ini di dekat bagian atas:

 use HelloPhoenix.Web, :model

Modul "HelloPhoenix.Web" terletak di "web/web.ex". Dalam modul, harus ada fungsi yang disebut "model" sebagai berikut:

 def model do quote do use Ecto.Schema import Ecto import Ecto.Changeset import Ecto.Query end end

Di sini Anda dapat melihat modul apa yang kami impor dari Ecto. Anda dapat menghapus atau menambahkan modul lain yang Anda inginkan di sini dan modul tersebut akan diimpor ke semua model.

Ada juga fungsi serupa seperti "tampilan" dan "pengontrol" yang masing-masing memiliki tujuan yang sama untuk tampilan dan pengontrol.

quote dan use kata kunci mungkin tampak membingungkan. Untuk contoh ini, Anda dapat menganggap kutipan sebagai langsung menjalankan kode itu dalam konteks modul yang memanggil fungsi itu. Jadi itu akan sama dengan menulis kode di dalam kutipan di modul.

Kata kunci use juga memungkinkan Anda menjalankan kode dalam konteks di mana kode itu dipanggil. Ini pada dasarnya membutuhkan modul yang ditentukan, kemudian memanggil makro __using__ pada modul yang menjalankannya dalam konteks di mana ia dipanggil. Anda dapat membaca lebih lanjut tentang kutipan dan penggunaan di panduan resmi.

Ini sangat membantu Anda memahami di mana fungsi-fungsi tertentu berada dalam kerangka kerja dan membantu mengurangi perasaan bahwa kerangka itu melakukan banyak "keajaiban".

Konkurensi adalah intinya.

Concurrency datang secara gratis di Phoenix karena ini adalah fitur utama dari Elixir. Anda mendapatkan aplikasi yang dapat menelurkan banyak proses dan berjalan pada banyak inti tanpa khawatir tentang keamanan dan keandalan utas.

Anda dapat menelurkan proses baru di Elixir ini dengan sederhana:

 spawn fn -> 1 + 2 end

Semuanya setelah spawn dan sebelum end akan berjalan dalam proses baru.

Faktanya, setiap permintaan di Phoenix ditangani dalam prosesnya sendiri. Elixir menggunakan kekuatan Erlang VM untuk menghadirkan konkurensi yang andal dan efisien “Gratis.”

Ini juga membuat Phoenix menjadi pilihan tepat untuk menjalankan layanan yang menggunakan WebSockets, karena WebSockets perlu memelihara koneksi terbuka antara klien dan server (yang berarti Anda perlu membangun aplikasi Anda sehingga dapat menangani kemungkinan ribuan koneksi bersamaan).

Persyaratan ini akan menambah banyak kerumitan pada proyek yang dibangun di atas Ruby on Rails, tetapi Phoenix dapat memenuhi persyaratan ini secara gratis melalui Elixir.

Jika Anda ingin menggunakan WebSockets di aplikasi Phoenix Anda, Anda harus menggunakan Saluran. Ini setara dengan ActionCable di Ruby on Rails, tetapi tidak terlalu rumit untuk disiapkan karena Anda tidak perlu menjalankan server terpisah.

Phoenix membuat pembuatan aplikasi web modern menjadi mudah.

Meskipun kami sebagian besar berfokus pada perbedaan, Phoenix memang memiliki beberapa kesamaan dengan Ruby on Rails.

Phoenix kira-kira mengikuti pola MVC yang sama dengan Ruby on Rails, jadi mencari tahu kode apa yang ada di mana seharusnya tidak sulit sekarang karena Anda tahu tentang perbedaan utama. Phoenix juga memiliki generator yang mirip dengan Ruby on Rails untuk membuat model, pengontrol, migrasi, dan banyak lagi.

Setelah mempelajari Elixir, Anda akan perlahan-lahan mendekati tingkat produktivitas Ruby on Rails setelah Anda merasa lebih nyaman dengan Phoenix.

Beberapa kali saya merasa tidak produktif adalah ketika saya menemukan masalah yang diselesaikan oleh permata di Ruby, dan saya tidak dapat menemukan perpustakaan serupa untuk Elixir. Untungnya, celah-celah ini perlahan-lahan diisi oleh komunitas Elixir yang berkembang.

Perbedaan di Phoenix membantu menyelesaikan banyak masalah yang datang dengan mengelola proyek Ruby on Rails yang kompleks. Meskipun mereka tidak menyelesaikan semua masalah, mereka membantu mendorong Anda ke arah yang benar. Memilih kerangka kerja yang lambat karena Anda menginginkan produktivitas bukan lagi alasan yang valid, Phoenix memungkinkan Anda memiliki keduanya.