Bisakah saya menggunakan Redux di server?

Redux adalah alat yang sangat baik untuk mengelola keadaan aplikasi front-end yang kompleks. Penulis materi, terjemahan yang kami terbitkan hari ini, akan menemukan jawaban untuk pertanyaan apakah mungkin untuk memanfaatkan fitur Redux di lingkungan server.
gambar

Mengapa saya membutuhkan perpustakaan Redux?


Situs pustaka Redux mengatakan bahwa itu adalah "wadah keadaan yang dapat diprediksi untuk aplikasi JavaScript." Redux biasanya disebut sebagai alat untuk mengelola keadaan aplikasi, dan meskipun perpustakaan ini terutama digunakan dengan React, itu dapat digunakan dalam proyek berbasis JavaScript apa pun.

Kami telah menyebutkan bahwa Redux digunakan untuk mengontrol keadaan aplikasi. Sekarang mari kita bicara tentang apa "kondisi" itu. Konsep ini agak sulit untuk didefinisikan, tetapi kami masih mencoba menggambarkannya.

Mempertimbangkan "keadaan", jika kita berbicara tentang orang atau tentang objek-objek dunia material, kita berusaha menggambarkan, pada kenyataannya, kondisi mereka pada titik di mana kita berbicara tentang mereka, mungkin mempertimbangkan satu atau lebih parameter. Misalnya, kita dapat mengatakan tentang danau: "Airnya sangat panas," atau: "Airnya beku." Dalam pernyataan ini, kami menggambarkan keadaan danau dalam hal suhu airnya.

Ketika seseorang berkata tentang dirinya sendiri: “Saya kandas,” ia mempertimbangkan jumlah uang yang ia miliki. Jelas bahwa dalam masing-masing contoh ini kita hanya berbicara tentang satu aspek dari keadaan benda. Tapi, dalam contoh tentang uang, pernyataan itu mungkin salah satu yang menggambarkan beberapa parameter: "Saya kandas, saya belum makan untuk waktu yang lama, tapi saya senang!". Sangat penting untuk dicatat di sini bahwa negara adalah sesuatu yang tidak kekal. Ini artinya bisa berubah. Oleh karena itu, ketika kita belajar tentang keadaan saat ini dari objek tertentu, kita memahami bahwa keadaan sebenarnya dapat berubah beberapa detik atau menit setelah kita mempelajarinya.

Ketika kita berurusan dengan program, beberapa fitur dikaitkan dengan konsep "negara". Pertama, keadaan aplikasi diwakili oleh data yang disimpan di suatu tempat. Sebagai contoh, data ini dapat disimpan dalam memori (katakanlah, sebagai objek JavaScript), tetapi dapat disimpan dalam file, dalam database, dan menggunakan beberapa mekanisme caching seperti Redis. Kedua, keadaan aplikasi biasanya terkait dengan contoh spesifiknya. Oleh karena itu, ketika kita berbicara tentang keadaan aplikasi, yang kita maksud adalah contoh spesifik dari aplikasi ini, sebuah proses, lingkungan kerja yang diatur dalam aplikasi untuk pengguna tertentu. Status aplikasi dapat mencakup, misalnya, informasi berikut:

  • Apakah pengguna masuk atau tidak? Jika demikian, berapa lama sesi berlangsung dan kapan akan berakhir?
  • Berapa poin yang skor pengguna? Pertanyaan semacam itu relevan, misalnya, untuk permainan tertentu.
  • Di mana tepatnya pengguna menghentikan video? Pertanyaan ini dapat ditanyakan tentang aplikasi pemutar video.

Jika kita berbicara tentang keadaan aplikasi di tingkat yang lebih rendah, maka itu dapat mencakup, misalnya, informasi berikut:

  • Variabel apa yang diatur dalam lingkungan saat ini di mana aplikasi berjalan (ini merujuk pada apa yang disebut "variabel lingkungan").
  • File apa yang sedang digunakan oleh program?

Melihat "snapshot" (mereka sering disebut "snapshots" - dari snapshot) dari status aplikasi kapan saja, kita dapat belajar tentang kondisi di mana aplikasi bekerja pada saat itu, dan, jika perlu, menciptakan kembali kondisi ini dengan aplikasi ke keadaan di mana ia menerima snapshot.

Negara dapat dimodifikasi selama eksekusi tindakan tertentu oleh pengguna. Misalnya, jika pengguna memindahkan karakter game dengan benar dalam game sederhana, ini dapat meningkatkan jumlah poin. Dalam aplikasi yang cukup kompleks, pendekatan untuk memodifikasi keadaan mungkin menjadi lebih rumit, perubahan keadaan mungkin berasal dari sumber yang berbeda.

Misalnya, dalam game multi pemain, berapa banyak poin yang skor pengguna tidak hanya bergantung pada tindakannya, tetapi juga pada tindakan orang-orang yang bermain dengannya di tim yang sama. Dan jika karakter yang dikendalikan komputer berhasil menyerang karakter permainan yang dikontrol pengguna, pengguna dapat kehilangan sejumlah poin.

Bayangkan kita sedang mengembangkan aplikasi front-end seperti PWA Twitter . Ini adalah aplikasi satu halaman di mana ada beberapa tab, misalnya - Beranda, Cari, Pemberitahuan, dan Pesan. Setiap tab tersebut memiliki ruang kerja sendiri, yang dimaksudkan untuk menampilkan informasi tertentu, dan untuk modifikasi. Semua data ini membentuk keadaan aplikasi. Jadi, tweet, pemberitahuan, dan pesan baru tiba di aplikasi setiap beberapa detik. Pengguna dapat bekerja dengan program dan dengan data ini. Misalnya, dia bisa membuat tweet atau menghapusnya, dia bisa me-retweet tweet, dia bisa membaca notifikasi, mengirim pesan ke seseorang, dan sebagainya. Segala sesuatu yang baru saja dibahas mengubah keadaan aplikasi.


Semua tab ini memiliki komponen komponen antarmuka pengguna yang digunakan untuk menampilkan dan memodifikasi data. Keadaan aplikasi dapat dipengaruhi oleh data yang memasukkan aplikasi dari luar, dan tindakan pengguna

Jelas bahwa dalam aplikasi seperti itu, sumber-sumber perubahan negara dapat berupa entitas yang berbeda, sedangkan perubahan yang diprakarsai oleh sumber yang berbeda dapat terjadi hampir secara bersamaan. Jika kita mengelola keadaan secara manual, mungkin sulit bagi kita untuk memantau apa yang terjadi. Kesulitan-kesulitan ini menyebabkan kontradiksi. Misalnya, tweet dapat dihapus, tetapi masih akan ditampilkan dalam aliran tweet. Atau, katakanlah, pengguna dapat membaca notifikasi atau pesan, tetapi masih akan ditampilkan dalam program sebagai tidak ditonton.

Pengguna dapat menyukai tweet, hati akan muncul di antarmuka program, tetapi permintaan jaringan yang mengirimkan informasi tentang suka ke server tidak akan berfungsi. Akibatnya, apa yang dilihat pengguna akan berbeda dari apa yang disimpan di server. Untuk mencegah situasi seperti itu Redux mungkin diperlukan.

Bagaimana cara kerja Redux?


Di pustaka Redux, ada tiga konsep utama yang bertujuan membuat manajemen keadaan aplikasi menjadi mudah dan mudah:

  1. Penyimpanan (store). Repositori Redux adalah objek JavaScript yang mewakili keadaan aplikasi. Ini memainkan peran "satu-satunya sumber data yang dapat diandalkan." Ini berarti bahwa seluruh aplikasi harus bergantung pada penyimpanan sebagai satu-satunya entitas yang bertanggung jawab untuk mewakili negara.
  2. Tindakan Toko negara hanya baca. Ini berarti tidak dapat dimodifikasi dengan mengaksesnya secara langsung. Satu-satunya cara untuk mengubah konten repositori adalah dengan menggunakan tindakan. Setiap komponen yang ingin mengubah status harus mengambil tindakan yang sesuai.
  3. Reduksi (reduksi), yang juga disebut "konverter". Peredam adalah fungsi murni yang menjelaskan bagaimana suatu negara dimodifikasi melalui tindakan. Peredam mengambil status dan tindakan saat ini, eksekusi yang diminta oleh komponen aplikasi tertentu, setelah itu mengembalikan keadaan yang diubah.

Menggunakan ketiga konsep ini berarti bahwa aplikasi tidak boleh lagi secara langsung memantau peristiwa yang merupakan sumber perubahan negara (tindakan pengguna, respons API, kejadian yang terkait dengan penerimaan data tertentu melalui protokol WebSocket, dan sebagainya) dan membuat keputusan tentang bagaimana Peristiwa ini akan mempengaruhi kondisi.

Dengan menggunakan model Redux, peristiwa ini dapat memicu tindakan yang akan mengubah status. Komponen yang perlu menggunakan data yang disimpan dalam keadaan aplikasi dapat berlangganan perubahan negara dan menerima informasi yang menarik bagi mereka. Dengan menggunakan semua mekanisme ini, Redux berkomitmen untuk membuat perubahan yang dapat diprediksi dalam kondisi aplikasi.

Berikut adalah contoh skematik yang menunjukkan bagaimana Anda dapat mengatur sistem manajemen negara sederhana menggunakan Redux dalam aplikasi fiksi kami:

import { createStore } from 'redux'; //  const tweets = (state = {tweets: []}, action) => {  switch (action.type) {    //     ,     .    case 'SHOW_NEW_TWEETS':      state.numberOfNewTweets = action.count;      return state.tweets.concat([action.tweets]);    default:      return state;  } }; //  ,     . SHOW_NEW_TWEETS const newTweetsAction = (tweets) => {  return {      type: 'SHOW_NEW_TWEETS',      tweets: tweets,      count: tweets.length  }; }; const store = createStore(tweets); twitterApi.fetchTweets()  .then(response => {    //  ,        ,    //    Redux.    store.dispatch(newTweetsAction(response.data));  }); //  ,    SHOW_NEW_TWEETS     //         . const postTweet = (text) => {  twitterApi.postTweet(text)  .then(response => {    store.dispatch(newTweetsAction([response.data]));  }); }; // ,  ,   WebSocket,   . //         . SHOW_NEW_TWEETS socket.on('newTweets', (tweets) => { store.dispatch(newTweetsAction(tweets)); }; //     ,  React,       , // ,         . //         , //    . store.subscribe(() => {  const { tweets } = store.getSTate();  render(tweets); }); 

Dengan mengambil kode ini sebagai dasar, kita dapat melengkapi sistem manajemen status aplikasi kita dengan tindakan tambahan dan mengirimkannya dari berbagai tempat aplikasi tanpa berisiko bingung.

Berikut adalah bahan dari mana Anda dapat mempelajari lebih lanjut tentang tiga prinsip dasar Redux.

Sekarang mari kita bicara tentang menggunakan Redux di lingkungan server.

Migrasi Prinsip Redux ke Lingkungan Server


Kami menjelajahi fitur-fitur Redux yang digunakan dalam mengembangkan aplikasi klien. Tapi, karena Redux adalah pustaka JavaScript, secara teori ia juga dapat digunakan di lingkungan server. Kami akan merefleksikan bagaimana prinsip-prinsip di atas dapat diterapkan di server.

Ingat bagaimana kita berbicara tentang bagaimana kondisi aplikasi klien? Perlu dicatat bahwa ada beberapa perbedaan konseptual antara aplikasi klien dan server. Jadi, aplikasi klien cenderung mempertahankan keadaan di antara berbagai peristiwa, katakanlah, antara eksekusi permintaan ke server. Aplikasi semacam itu disebut aplikasi stateful.

Jika mereka tidak berusaha untuk menyimpan keadaan, maka, misalnya, ketika bekerja dengan layanan web tertentu yang memerlukan login dan kata sandi, pengguna harus melakukan prosedur ini setiap kali ia pergi ke halaman baru dari antarmuka web yang sesuai.

Aplikasi Backend, di sisi lain, berusaha untuk tidak menyimpan status (mereka juga disebut aplikasi stateless). Di sini, berbicara tentang "aplikasi backend", kami terutama maksud proyek berdasarkan API tertentu yang terpisah dari aplikasi front-end. Ini berarti bahwa informasi tentang keadaan sistem harus disediakan untuk aplikasi serupa setiap kali diakses. Misalnya, API tidak memantau apakah pengguna masuk atau tidak. Itu menentukan statusnya dengan menganalisis token otentikasi dalam permintaannya ke API ini.

Ini membawa kita ke alasan penting mengapa Redux hampir tidak akan digunakan pada server dalam bentuk yang kami jelaskan kemampuannya di atas.

Faktanya adalah bahwa Redux dirancang untuk menyimpan keadaan aplikasi sementara. Tetapi keadaan aplikasi yang disimpan di server biasanya harus ada cukup lama. Jika Anda akan menggunakan repositori Redux di aplikasi Node.js sisi server Anda, keadaan aplikasi ini akan dihapus setiap kali proses node berhenti. Dan jika kita berbicara tentang server PHP yang mengimplementasikan skema manajemen negara yang serupa, maka negara akan dihapus ketika setiap permintaan baru tiba di server.

Situasi ini bahkan lebih rumit jika kita mempertimbangkan aplikasi server dalam hal skalabilitasnya. Jika Anda harus skala aplikasi secara horizontal, meningkatkan jumlah server, maka Anda akan memiliki banyak proses Node.js berjalan pada saat yang sama, dan masing-masing dari mereka akan memiliki opsi status sendiri. Ini berarti bahwa dengan penerimaan simultan dua permintaan yang identik ke backend, jawaban yang berbeda dapat diberikan.

Bagaimana menerapkan prinsip-prinsip manajemen negara yang kita bahas di server? Mari kita lihat lagi konsep-konsep Redux dan lihat bagaimana mereka biasanya digunakan di lingkungan server:

  1. Repositori. Di backend, "satu-satunya sumber data yang dapat diandalkan" biasanya adalah database. Terkadang, untuk memfasilitasi akses ke data yang sering diperlukan, atau karena alasan lain, salinan dari beberapa bagian dari basis data ini dapat dibuat - dalam bentuk cache atau dalam bentuk file. Biasanya, salinan seperti itu hanya untuk dibaca. Mekanisme yang mengendalikannya berlangganan perubahan di repositori utama, dan, ketika perubahan tersebut terjadi, perbarui konten salinan.
  2. Tindakan dan reduksi. Mereka adalah satu-satunya mekanisme yang digunakan untuk mengubah keadaan. Dalam sebagian besar aplikasi backend, kode ini ditulis dalam gaya imperatif, yang tidak kondusif untuk penggunaan konsep aksi dan reduksi.

Pertimbangkan dua pola desain yang, menurut sifatnya, mirip dengan apa yang ingin dilakukan perpustakaan Redux. Ini adalah CQRS dan Event Sourcing. Mereka, pada kenyataannya, muncul sebelum Redux, implementasi mereka bisa sangat sulit, jadi kita akan membicarakannya secara singkat.

Sumber CQRS dan Acara


CQRS (Command Query Responsibility Segregation) adalah pola desain, di mana aplikasi membaca data dari toko hanya menggunakan kueri, dan menulis hanya menggunakan perintah.

Saat menggunakan CQRS, satu-satunya cara untuk mengubah status aplikasi adalah dengan mengirim perintah. Perintah serupa dengan tindakan Redux. Misalnya, di Redux, Anda dapat menulis kode yang cocok dengan skema ini:

 const action = { type: 'CREATE_NEW_USER', payload: ... }; store.dispatch(action); //      const createUser = (state = {}, action) => { // }; 

Saat menggunakan CQRS, sesuatu seperti ini akan terlihat seperti ini:

 //      class Command { handle() { } } class CreateUserCommand extends Command { constructor(user) {   super();   this.user = user; } handle() {   //        } } const createUser = new CreateUserCommand(user); //   (   handle()) dispatch(createUser); //      CommandHandler commandHandler.handle(createUser); 

Kueri adalah mekanisme pembacaan data dalam templat CQRS. Mereka sama dengan konstruk store.getState() . Dalam implementasi CQRS yang sederhana, kueri akan berinteraksi langsung dengan database, mengambil catatan darinya.

Templat Sumber Acara dirancang untuk merekam semua perubahan dalam kondisi aplikasi sebagai urutan peristiwa. Template ini paling cocok untuk aplikasi yang perlu tahu tidak hanya tentang keadaan mereka saat ini, tetapi juga tentang sejarah perubahannya, tentang bagaimana aplikasi mencapai keadaan saat ini. Sebagai contoh di sini Anda dapat mengutip sejarah operasi dengan rekening bank, melacak paket, bekerja dengan pesanan di toko online, organisasi transportasi kargo, logistik.

Berikut ini adalah contoh penerapan templat Pengadaan Acara:

 //    Event Sourcing function transferMoneyBetweenAccounts(amount, fromAccount, toAccount) {   BankAccount.where({ id: fromAccount.id })     .decrement({ amount });   BankAccount.where({ id: toAccount.id })     .increment({ amount }); } function makeOnlinePayment(account, amount) {   BankAccount.where({ id: account.id })     .decrement({ amount }); } //    Event Sourcing function transferMoneyBetweenAccounts(amount, fromAccount, toAccount) {   dispatchEvent(new TransferFrom(fromAccount, amount, toAccount));   dispatchEvent(new TransferTo(toAccount, amount, fromAccount)); } function makeOnlinePayment(account, amount) {   dispatchEvent(new OnlinePaymentFrom(account, amount)); } class TransferFrom extends Event {   constructor(account, amount, toAccount) {     this.account = account;     this.amount = amount;     this.toAccount = toAccount;   }     handle() {     //    OutwardTransfer        OutwardTransfer.create({ from: this.account, to: this.toAccount, amount: this.amount, date: Date.now() });         //          BankAccount.where({ id: this.account.id })       .decrement({ amount: this.amount });   } } class TransferTo extends Event {   constructor(account, amount, fromAccount) {     this.account = account;     this.amount = amount;     this.fromAccount = fromAccount;   }     handle() {     //    InwardTransfer        InwardTransfer.create({ from: this.fromAccount, to: this.account, amount: this.amount, date: Date.now() });         //          BankAccount.where({ id: this.account.id })       .increment({ amount: this.amount });   } } class OnlinePaymentFrom extends Event {   constructor(account, amount) {     this.account = account;     this.amount = amount;   }     handle() {     //    OnlinePayment        OnlinePayment.create({ from: this.account, amount: this.amount, date: Date.now() });         //          BankAccount.where({ id: this.account.id })       .decrement({ amount: this.amount });   } } 

Apa yang terjadi di sini juga menyerupai bekerja dengan tindakan Redux.

Namun, mekanisme pendaftaran acara juga mengatur penyimpanan informasi jangka panjang tentang setiap perubahan negara, dan bukan hanya penyimpanan negara itu sendiri. Ini memungkinkan kami untuk mereproduksi perubahan ini ke titik waktu yang kami butuhkan, sehingga mengembalikan konten keadaan aplikasi pada saat ini. Misalnya, jika kita perlu memahami berapa banyak uang yang ada di rekening bank untuk tanggal tertentu, kita hanya perlu mereproduksi peristiwa yang terjadi dengan rekening bank sampai kita mencapai tanggal yang tepat. Peristiwa dalam kasus ini diwakili oleh penerimaan dana ke rekening dan mendebetnya dari dana, mendebit komisi bank dan operasi serupa lainnya. Jika kesalahan terjadi (yaitu, ketika peristiwa yang mengandung data yang salah terjadi), kami dapat membatalkan kondisi aplikasi saat ini, memperbaiki data terkait dan kembali ke kondisi aplikasi saat ini, sekarang telah dihasilkan tanpa kesalahan.

Template CQRS dan Event Sourcing sering digunakan bersama. Dan, yang menarik, Redux, pada kenyataannya, sebagian didasarkan pada template ini. Perintah dapat ditulis sehingga mereka, ketika dipanggil, mengirim acara. Kemudian peristiwa berinteraksi dengan repositori (basis data) dan memperbarui status. Dalam aplikasi waktu nyata, objek kueri juga dapat mendengarkan acara dan menerima informasi status yang diperbarui dari repositori.

Menggunakan salah satu templat ini dalam aplikasi sederhana dapat menyulitkannya. Namun, dalam kasus aplikasi yang dibangun untuk memecahkan masalah bisnis yang kompleks, CQRS dan Event Sourcing adalah abstraksi yang kuat yang membantu untuk memodelkan bidang subjek aplikasi tersebut dengan lebih baik dan meningkatkan manajemen negara mereka.

Harap dicatat bahwa pola CQRS dan Pengadaan Acara dapat diimplementasikan dengan cara yang berbeda, sementara beberapa implementasinya lebih kompleks daripada yang lain. Kami hanya mempertimbangkan contoh penerapannya yang sangat sederhana. Jika Anda menulis aplikasi server di Node.js, lihat wolkenkit . Kerangka kerja ini, di antara apa yang ditemukan di bidang ini, menyediakan pengembang dengan salah satu antarmuka paling sederhana untuk mengimplementasikan template CQRS dan Event Sourcing.

Ringkasan


Redux adalah alat yang hebat untuk mengelola status aplikasi, agar perubahan status dapat diprediksi. Pada artikel ini, kami berbicara tentang konsep-konsep kunci dari perpustakaan ini dan menemukan bahwa walaupun menggunakan Redux di lingkungan server mungkin bukan ide yang baik, Anda dapat menerapkan prinsip-prinsip serupa di server menggunakan templat CQRS dan Event Sourcing .

Pembaca yang budiman! Bagaimana Anda mengatur manajemen negara aplikasi klien dan server?

Source: https://habr.com/ru/post/id437804/


All Articles