ร้านค้าฟลักซ์ควรหรือการกระทำ (หรือทั้งสองอย่าง) สัมผัสบริการภายนอกหรือไม่?


122

หากร้านค้ารักษาสถานะของตัวเองและมีความสามารถในการเรียกใช้บริการเครือข่ายและการจัดเก็บข้อมูลในการดำเนินการดังกล่าว ... ในกรณีนี้การกระทำเป็นเพียงผู้ส่งข้อความที่โง่เขลา

-หรือ-

... ร้านค้าควรเป็นผู้รับข้อมูลที่ไม่เปลี่ยนรูปแบบเป็นใบ้จากการดำเนินการ (และการดำเนินการเป็นข้อมูลที่ดึง / ส่งข้อมูลระหว่างแหล่งภายนอกหรือไม่การจัดเก็บในอินสแตนซ์นี้จะทำหน้าที่เป็นมุมมองแบบจำลองและจะสามารถรวม / กรองข้อมูลได้ ข้อมูลก่อนที่จะตั้งฐานสถานะของตนเองบนข้อมูลที่ไม่เปลี่ยนรูปที่ถูกป้อนโดยการกระทำ

สำหรับฉันแล้วดูเหมือนว่ามันควรจะเป็นอย่างใดอย่างหนึ่ง (แทนที่จะผสมทั้งสองอย่าง) ถ้าเป็นเช่นนั้นเหตุใดจึงเป็นที่ต้องการ / แนะนำมากกว่าอีกรายการหนึ่ง?


2
โพสต์นี้อาจช่วยได้code-experience.com/…
Markus-ipse

สำหรับผู้ที่ประเมินการใช้งานรูปแบบฟลักซ์ต่างๆฉันขอแนะนำอย่างยิ่งให้ดูที่ Redux github.com/rackt/redux Stores จะถูกนำไปใช้เป็นฟังก์ชันบริสุทธิ์ที่อยู่ในสถานะปัจจุบันและปล่อยเวอร์ชันใหม่ของสถานะนั้น เนื่องจากพวกเขาทำหน้าที่อย่างแท้จริงคำถามที่ว่าพวกเขาสามารถโทรหาเครือข่ายได้หรือไม่และบริการจัดเก็บข้อมูลนั้นไม่อยู่ในมือคุณพวกเขาทำไม่ได้
plaxdan

คำตอบ:


151

ฉันเคยเห็นรูปแบบฟลักซ์ใช้ทั้งสองวิธีและหลังจากทำทั้งสองอย่างด้วยตัวเองแล้ว (เริ่มต้นด้วยวิธีการเดิม) ฉันเชื่อว่าร้านค้าควรเป็นผู้รับข้อมูลที่โง่เขลาจากการกระทำและการประมวลผลการเขียนแบบอะซิงโครนัสควรอยู่ใน ผู้สร้างการกระทำ (การอ่าน Async สามารถจัดการได้แตกต่างกัน ) จากประสบการณ์ของฉันสิ่งนี้มีประโยชน์บางประการตามลำดับความสำคัญ:

  1. ร้านค้าของคุณเป็นแบบซิงโครนัสอย่างสมบูรณ์ สิ่งนี้ทำให้ตรรกะของร้านค้าของคุณง่ายต่อการปฏิบัติตามและทดสอบได้ง่ายมากเพียงแค่สร้างอินสแตนซ์ร้านค้าด้วยสถานะที่กำหนดส่งการดำเนินการและตรวจสอบเพื่อดูว่าสถานะเปลี่ยนไปตามที่คาดไว้หรือไม่ นอกจากนี้แนวคิดหลักประการหนึ่งของฟลักซ์คือการป้องกันการจัดส่งแบบเรียงซ้อนและเพื่อป้องกันการจัดส่งหลายครั้งพร้อมกัน สิ่งนี้ทำได้ยากมากเมื่อร้านค้าของคุณทำการประมวลผลแบบอะซิงโครนัส

  2. การจัดส่งการกระทำทั้งหมดเกิดขึ้นจากผู้สร้างการกระทำหากคุณจัดการการดำเนินการแบบอะซิงโครนัสในร้านค้าของคุณและคุณต้องการให้ตัวจัดการการดำเนินการของร้านค้าของคุณเป็นแบบซิงโครนัส (และคุณควรได้รับการรับประกันการจัดส่งครั้งเดียวแบบฟลักซ์) ร้านค้าของคุณจะต้องเริ่มการดำเนินการ SUCCESS และ FAIL เพิ่มเติมเพื่อตอบสนองต่ออะซิงโครนัส การประมวลผล การใส่การจัดส่งเหล่านี้ในผู้สร้างแอ็คชั่นแทนที่จะช่วยแยกงานของผู้สร้างการกระทำและร้านค้า ยิ่งไปกว่านั้นคุณไม่จำเป็นต้องไปหาตรรกะร้านค้าของคุณเพื่อดูว่าการดำเนินการถูกส่งมาจากที่ใด การกระทำแบบอะซิงโครนัสทั่วไปในกรณีนี้อาจมีลักษณะดังนี้ (เปลี่ยนไวยากรณ์ของการdispatchโทรตามรสชาติของฟลักซ์ที่คุณใช้):

    someActionCreator: function(userId) {
      // Dispatch an action now so that stores that want
      // to optimistically update their state can do so.
      dispatch("SOME_ACTION", {userId: userId});
    
      // This example uses promises, but you can use Node-style
      // callbacks or whatever you want for error handling.
      SomeDataAccessLayer.doSomething(userId)
      .then(function(newData) {
        // Stores that optimistically updated may not do anything
        // with a "SUCCESS" action, but you might e.g. stop showing
        // a loading indicator, etc.
        dispatch("SOME_ACTION_SUCCESS", {userId: userId, newData: newData});
      }, function(error) {
        // Stores can roll back by watching for the error case.
        dispatch("SOME_ACTION_FAIL", {userId: userId, error: error});
      });
    }

    ตรรกะที่อาจซ้ำกันในการดำเนินการต่างๆควรแยกออกเป็นโมดูลแยกต่างหาก ในตัวอย่างนี้โมดูลนั้นจะเป็นSomeDataAccessLayerซึ่งจัดการกับการร้องขอ Ajax จริง

  3. คุณต้องการผู้สร้างแอคชั่นน้อยลง นี่ไม่ใช่เรื่องใหญ่ แต่ก็น่ายินดี ตามที่กล่าวไว้ใน # 2 หากร้านค้าของคุณมีการจัดการการจัดส่งการดำเนินการแบบซิงโครนัส (และควร) คุณจะต้องเริ่มการดำเนินการเพิ่มเติมเพื่อจัดการกับผลลัพธ์ของการดำเนินการแบบอะซิงโครนัส การดำเนินการจัดส่งในผู้สร้างการดำเนินการหมายความว่าผู้สร้างการกระทำเดียวสามารถส่งการดำเนินการทั้งสามประเภทได้โดยจัดการผลของการเข้าถึงข้อมูลแบบอะซิงโครนัสเอง


15
ฉันคิดว่าสิ่งที่มาของการเรียก API ของเว็บ (ผู้สร้างการดำเนินการเทียบกับร้านค้า) มีความสำคัญน้อยกว่าความจริงที่ว่าการเรียกกลับที่สำเร็จ / ข้อผิดพลาดควรสร้างการดำเนินการ ดังนั้นกระแสข้อมูลจึงอยู่เสมอ: action -> dispatcher -> stores -> views
fisherwebdev

1
การวางตรรกะของคำขอจริงภายในโมดูล API จะดีกว่า / ทดสอบง่ายกว่าไหม ดังนั้นโมดูล API ของคุณก็สามารถคืนสัญญาที่คุณส่งมาได้ ผู้สร้างการดำเนินการเพียงแค่จัดส่งตามการแก้ไข / ล้มเหลวหลังจากส่งการดำเนินการ "รอดำเนินการ" เริ่มต้น คำถามที่ยังคงอยู่คือองค์ประกอบรับฟัง 'เหตุการณ์' เหล่านี้อย่างไรเนื่องจากฉันไม่แน่ใจว่าสถานะคำขอควรจับคู่กับสถานะการจัดเก็บ
backdesk

@backdesk นั่นคือสิ่งที่ฉันทำในตัวอย่างด้านบน: ส่งการดำเนินการที่รอดำเนินการเริ่มต้น ( "SOME_ACTION") ใช้ API เพื่อส่งคำขอ ( SomeDataAccessLayer.doSomething(userId)) ซึ่งส่งคืนคำสัญญาและในสอง.thenฟังก์ชันให้ส่งการดำเนินการเพิ่มเติม สถานะคำขอสามารถ (มากหรือน้อย) แมปเพื่อจัดเก็บสถานะหากแอปพลิเคชันต้องการทราบเกี่ยวกับสถานะของรัฐ แผนที่นี้ขึ้นอยู่กับแอปอย่างไร (เช่นความคิดเห็นแต่ละรายการอาจมีสถานะข้อผิดพลาดส่วนบุคคล a la Facebook หรืออาจมีองค์ประกอบข้อผิดพลาดส่วนกลาง)
Michelle Tilley

@MichelleTilley "หนึ่งในแนวคิดหลักในฟลักซ์คือการป้องกันการจัดส่งแบบเรียงซ้อนและป้องกันการจัดส่งหลายรายการพร้อมกันซึ่งเป็นเรื่องยากมากที่จะทำเมื่อร้านค้าของคุณทำการประมวลผลแบบอะซิงโครนัส" นั่นคือประเด็นสำคัญสำหรับฉัน พูดได้ดี.

51

ฉันทวีตคำถามนี้ไปยังผู้พัฒนาที่ Facebook และคำตอบที่ฉันได้รับจาก Bill Fisher คือ:

เมื่อตอบสนองต่อการโต้ตอบของผู้ใช้กับ UI ฉันจะทำการเรียก async ในเมธอดผู้สร้างการดำเนินการ

แต่เมื่อคุณมีทิกเกอร์หรือไดรเวอร์อื่น ๆ ที่ไม่ใช่มนุษย์การโทรจากร้านค้าจะทำงานได้ดีขึ้น

สิ่งสำคัญคือการสร้างการดำเนินการในการเรียกกลับข้อผิดพลาด / ความสำเร็จดังนั้นข้อมูลจึงเกิดขึ้นจากการกระทำเสมอ


ในขณะนี้มีเหตุผลความคิดใด ๆ ทำไมa call from store works better when action triggers from non-human driver ?
SharpCoder

@SharpCoder ฉันเดาว่าถ้าคุณมีสัญลักษณ์แสดงสดหรืออะไรที่คล้ายกันคุณไม่จำเป็นต้องเริ่มการดำเนินการและเมื่อคุณทำเช่นนั้นจากร้านค้าคุณอาจต้องเขียนโค้ดน้อยลงเนื่องจากร้านค้าสามารถเข้าถึงสถานะได้ทันที & แสดงการเปลี่ยนแปลง
Florian Wendelborn

8

ร้านค้าควรทำทุกอย่างรวมถึงการดึงข้อมูลและส่งสัญญาณไปยังส่วนประกอบว่าข้อมูลของร้านค้าได้รับการอัปเดตแล้ว ทำไม? เนื่องจากการกระทำอาจมีน้ำหนักเบาใช้แล้วทิ้งและเปลี่ยนได้โดยไม่ส่งผลต่อพฤติกรรมที่สำคัญ พฤติกรรมและฟังก์ชันการทำงานที่สำคัญทั้งหมดเกิดขึ้นในร้านค้า นอกจากนี้ยังป้องกันการทำซ้ำของพฤติกรรมที่อาจถูกคัดลอกในการกระทำที่เหมือนกัน แต่แตกต่างกันสองอย่าง ร้านค้าเป็นแหล่งเดียวของ (การจัดการความจริง) ของคุณ

ในการใช้งาน Flux ทุกครั้งฉันเคยเห็น Actions เป็นสตริงเหตุการณ์ที่กลายเป็นวัตถุเช่นเดิมคุณจะมีเหตุการณ์ชื่อ "anchor: clicked" แต่ใน Flux จะถูกกำหนดเป็น AnchorActions.Clicked พวกเขา "โง่" มากที่การใช้งานส่วนใหญ่มีวัตถุ Dispatcher แยกต่างหากเพื่อส่งเหตุการณ์ไปยังร้านค้าที่กำลังฟังอยู่

โดยส่วนตัวแล้วฉันชอบการใช้งาน Flux ของ Reflux ที่ไม่มีวัตถุ Dispatcher แยกต่างหากและวัตถุ Action จะทำการจัดส่งเอง


แก้ไข: Flux ของ Facebook ดึง "ผู้สร้างการกระทำ" มาใช้จริงดังนั้นพวกเขาจึงใช้การกระทำที่ชาญฉลาด พวกเขายังเตรียมน้ำหนักบรรทุกโดยใช้ร้านค้า:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/actions/ChatMessageActionCreators.js#L27 (บรรทัด 27 และ 28)

การเรียกกลับเมื่อเสร็จสิ้นจะทริกเกอร์การดำเนินการใหม่ในครั้งนี้ด้วยข้อมูลที่ดึงมาเป็นเพย์โหลด:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51

ฉันเดาว่านั่นเป็นทางออกที่ดีกว่า


การติดตั้ง Reflux นี้คืออะไร? ฉันไม่เคยได้ยินเรื่องนี้ คำตอบของคุณน่าสนใจ คุณหมายความว่าการติดตั้งร้านค้าของคุณควรมีตรรกะในการเรียก API และอื่น ๆ ? ฉันคิดว่าร้านค้าควรได้รับข้อมูลและอัปเดตค่าของพวกเขา พวกเขากรองการกระทำที่เฉพาะเจาะจงและอัปเดตคุณลักษณะบางอย่างของร้านค้าของตน
Jeremy D

Reflux เป็นรูปแบบเล็กน้อยของ Flux ของ Facebook: github.com/spoike/refluxjs Stores จัดการโดเมน "Model" ทั้งหมดของแอปพลิเคชันของคุณเทียบกับ Actions / Dispatchers ที่เย็บและเชื่อมสิ่งต่างๆเข้าด้วยกันเท่านั้น
Rygu

1
ดังนั้นฉันจึงคิดถึงเรื่องนี้มากขึ้นและ (เกือบ) ตอบคำถามของตัวเองแล้ว ฉันจะเพิ่มมันเป็นคำตอบที่นี่ (เพื่อให้คนอื่นโหวต) แต่เห็นได้ชัดว่าฉันมีกรรม - ยากจนเกินไปที่ stackoverflow จะสามารถโพสต์คำตอบได้ นี่คือลิงค์: groups.google.com/d/msg/reactjs/PpsvVPvhBbc/BZoG-bFeOwoJ
plaxdan

ขอบคุณสำหรับลิงก์กลุ่ม Google ดูเหมือนว่าให้ข้อมูลจริงๆ ฉันยังเป็นแฟนตัวยงของทุกสิ่งที่เกิดขึ้นในดิสแพตเชอร์และตรรกะง่ายๆในร้านโดยพื้นฐานแล้วการอัปเดตข้อมูลของพวกเขานั่นคือทั้งหมด @Rygu ฉันจะตรวจกรดไหลย้อน
Jeremy D

ฉันแก้ไขคำตอบด้วยมุมมองอื่น ดูเหมือนว่าทั้งสองวิธีจะเป็นไปได้ ฉันเกือบจะเลือกวิธีแก้ปัญหาของ Facebook มากกว่าคนอื่น ๆ
Rygu

3

ฉันจะโต้แย้งเพื่อสนับสนุนการกระทำที่ "โง่"

การกำหนดความรับผิดชอบในการรวบรวมข้อมูลการดูใน Actions จะทำให้คุณจับคู่ Actions กับข้อกำหนดด้านข้อมูลของมุมมองของคุณ

ในทางตรงกันข้ามการดำเนินการทั่วไปซึ่งอธิบายถึงเจตนาของผู้ใช้อย่างเปิดเผยหรือการเปลี่ยนสถานะบางอย่างในแอปพลิเคชันของคุณอนุญาตให้ Store ใด ๆ ที่ตอบสนองต่อการดำเนินการนั้นเปลี่ยนความตั้งใจให้เป็นสถานะที่ปรับแต่งโดยเฉพาะสำหรับมุมมองที่สมัครรับข้อมูล

สิ่งนี้ให้ยืมตัวเองไปยังร้านค้าเฉพาะทางจำนวนมากขึ้น แต่มีขนาดเล็กกว่า ฉันเถียงสำหรับสไตล์นี้เพราะ

  • สิ่งนี้ช่วยให้คุณมีความยืดหยุ่นมากขึ้นในการดูใช้ข้อมูล Store
  • ร้านค้าที่ "ฉลาด" ซึ่งเชี่ยวชาญเฉพาะสำหรับการดูที่ดูจะมีขนาดเล็กลงและใช้คู่กับแอปที่ซับซ้อนน้อยกว่าการดำเนินการ "อัจฉริยะ" ซึ่งอาจขึ้นอยู่กับการดูจำนวนมาก

จุดประสงค์ของ Store คือการให้ข้อมูลแก่การดู ชื่อ "Action" แนะนำให้ฉันทราบว่ามีวัตถุประสงค์เพื่ออธิบายการเปลี่ยนแปลงในแอปพลิเคชันของฉัน

สมมติว่าคุณต้องเพิ่มวิดเจ็ตในมุมมองแดชบอร์ดที่มีอยู่ซึ่งจะแสดงข้อมูลรวมใหม่ ๆ ที่ทีมแบ็กเอนด์ของคุณเพิ่งเปิดตัว

ด้วยการดำเนินการ "อัจฉริยะ" คุณอาจต้องเปลี่ยนการดำเนินการ "รีเฟรชแดชบอร์ด" เพื่อใช้ API ใหม่ อย่างไรก็ตาม "การรีเฟรชแดชบอร์ด" ในแง่นามธรรมไม่ได้เปลี่ยนแปลง ข้อกำหนดด้านข้อมูลของมุมมองของคุณคือสิ่งที่เปลี่ยนแปลงไป

ด้วยการดำเนินการ "โง่" คุณอาจเพิ่ม Store ใหม่สำหรับวิดเจ็ตใหม่ที่จะใช้และตั้งค่าเพื่อให้เมื่อได้รับประเภทการดำเนินการ "refresh-dashboard" จะส่งคำขอสำหรับข้อมูลใหม่และเปิดเผยไปยัง วิดเจ็ตใหม่เมื่อพร้อม มันสมเหตุสมผลสำหรับฉันที่เมื่อเลเยอร์มุมมองต้องการข้อมูลมากขึ้นหรือแตกต่างกันสิ่งที่ฉันเปลี่ยนแปลงคือแหล่งที่มาของข้อมูล: ร้านค้า


2

flux-react-router-demoของ gaeron มีรูปแบบยูทิลิตี้ที่ดีของแนวทางที่ 'ถูกต้อง'

ActionCreator สร้างสัญญาจากบริการ API ภายนอกจากนั้นส่งผ่านคำสัญญาและค่าคงที่การดำเนินการสามค่าไปยังdispatchAsyncฟังก์ชันในตัวจัดการพร็อกซี / ขยาย dispatchAsyncจะส่งการดำเนินการแรกเช่น 'GET_EXTERNAL_DATA' เสมอและเมื่อคำสัญญาส่งคืนมันจะส่ง 'GET_EXTERNAL_DATA_SUCCESS' หรือ 'GET_EXTERNAL_DATA_ERROR'


1

หากคุณต้องการให้วันหนึ่งมีสภาพแวดล้อมการพัฒนาที่เทียบเคียงได้กับสิ่งที่คุณเห็นในวิดีโอที่มีชื่อเสียงของ Bret Victor Inventing on Principleคุณควรใช้ร้านค้าใบ้ที่เป็นเพียงการฉายภาพการกระทำ / เหตุการณ์ภายในโครงสร้างข้อมูลโดยไม่มีผลข้างเคียงใด ๆ นอกจากนี้ยังจะช่วยถ้าร้านค้าของคุณเป็นจริงสมาชิกของโครงสร้างข้อมูลเดียวกันทั่วโลกไม่เปลี่ยนรูปเหมือนในRedux

คำอธิบายเพิ่มเติมที่นี่: https://stackoverflow.com/a/31388262/82609

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.