CAFECA-IO / TideBit-DeFi

TideBit Decentralize Finance Version
https://tidebit-defi.vercel.app
GNU General Public License v3.0
1 stars 0 forks source link

規劃使用 Zustand 重構的工作分配 #1447

Closed arealclimber closed 9 months ago

arealclimber commented 9 months ago

根據現有程式碼功能,依照 Zustand 規劃重構工作

References

arealclimber commented 9 months ago

畫圖網站

原本架構的 sequence diagram

original

title TBD Frontend Original Pattern (e.g. Candlestick chart)

participant Backend
participant Pusher Instance
participant Web Worker Instance
participant Browser
participant App (Context)
participant Worker (Context)
participant Market (Context)
participant Candlestick Chart Component

Browser->App (Context):execute
note over Market (Context),App (Context):Initialize default context data
Market (Context)->Candlestick Chart Component:marketCtx.candlestickChartData

activate Market (Context)

Candlestick Chart Component->Candlestick Chart Component:drawChart()
deactivate Market (Context)

App (Context)->Worker (Context):workerCtx.init()
activate App (Context)
activate Worker (Context)

Worker (Context)->Worker (Context):workerCtx.init() {\ninitPusher()\ninitAPIWorker()\ninitJobQueue()\n}\n

Worker (Context)->Web Worker Instance:apiWorker = createWorkerInstance()\n

activate Web Worker Instance
activate Web Worker Instance

Pusher Instance<-Worker (Context):pusher = createPusherInstance()
deactivate Worker (Context)
deactivate App (Context)

activate Pusher Instance
App (Context)->Market (Context):marketCtx.init()
activate App (Context)

activate Market (Context)

Web Worker Instance<-Worker (Context):apiWorker.postMessage(request)

Market (Context)->Market (Context):marketCtx.init() {\nworkerCtx.requestHandler("currencies")\nworkerCtx.requestHandler("tickers")\nworkerCtx.requestHandler("guaranteed-stop-fee")\n}
Worker (Context)<--Market (Context):requestHandler() {\n createJob()\n apiWorker.onmessage()\n}
activate Market (Context)
activate Worker (Context)
Web Worker Instance-->Worker (Context):apiWorker.onmessage(result)
deactivate Web Worker Instance
Worker (Context)-->Market (Context):result

deactivate Market (Context)
deactivate Market (Context)

deactivate App (Context)
deactivate Worker (Context)

activate Web Worker Instance

activate Market (Context)

Market (Context)->Market (Context):marketCtx.selectTickerHandler() {\nworkerCtx.requestHandler("trades")\nworkerCtx.requestHandler("ticker-static")\nworkerCtx.requestHandler("ticker-live-statistics")\n}\n

Web Worker Instance<-Worker (Context):apiWorker.postMessage(request)

Worker (Context)<--Market (Context):requestHandler() {\n createJob()\n apiWorker()\n}
activate Market (Context)
activate Worker (Context)
Web Worker Instance-->Worker (Context):apiWorker.onmessage(result)
deactivate Web Worker Instance

Worker (Context)-->Market (Context):result
deactivate Market (Context)

deactivate Market (Context)
deactivate Worker (Context)

Backend->Pusher Instance:send("Trades")
Pusher Instance->Worker (Context):pusher.emit("Trades")
activate Pusher Instance

activate Worker (Context)
Market (Context)->Market (Context):setInterval(() => {\nconvertTradesToCandlesticks(); \nsetCandlestickData();\n}, 100)

Market (Context)->Candlestick Chart Component:marketCtx.candlestickChartData\n(every 0.1 secs)
activate Market (Context)
Candlestick Chart Component->Candlestick Chart Component:drawChart()
deactivate Market (Context)
Worker (Context)->Worker (Context):pusher.on("Trades", event.emit("Trades"))
deactivate Pusher Instance

Worker (Context)->Market (Context):event.emit("Trades")\n\n
deactivate Worker (Context)

activate Market (Context)
Market (Context)->Market (Context):event.on("Trades", addTrades())\n
deactivate Market (Context)

使用 Zustand 可能無法保有原本的 Worker 架構 [待測試]

question with Zustand

title TBD Frontend Original Pattern (e.g. Candlestick chart)

participant Backend
participant Pusher Instance
participant Web Worker Instance
participant Browser
participant App (Context)
participant Worker (Context)
participant Market (Context)
participant Candlestick Chart Component

Browser->App (Context):execute
note over Market (Context),App (Context):Initialize default context data
Market (Context)->Candlestick Chart Component:marketCtx.candlestickChartData

activate Market (Context)

Candlestick Chart Component->Candlestick Chart Component:drawChart()
deactivate Market (Context)

App (Context)->Worker (Context):workerCtx.init()
activate App (Context)
activate Worker (Context)

Worker (Context)->Worker (Context):workerCtx.init() {\ninitPusher()\ninitAPIWorker()\ninitJobQueue()\n}\n

Worker (Context)->Web Worker Instance:apiWorker = createWorkerInstance()\n

activate Web Worker Instance
activate Web Worker Instance

Pusher Instance<-Worker (Context):pusher = createPusherInstance()
deactivate Worker (Context)
deactivate App (Context)

activate Pusher Instance
App (Context)->Market (Context):marketCtx.init()
activate App (Context)

activate Market (Context)

Web Worker Instance<-Worker (Context):apiWorker.postMessage(request)

note over Market (Context) #b0ceff:用 Zustand 重構的話,Market Store 可能不能調用 Worker Store 的 API request function 來處理 API ,也就無法直接在 Market Store 更新資料,需要在 Component 層級處理 API 並更新全局狀態(Store)
note over Market (Context)#b0ceff :(作者推薦使用單一 store,但似乎有其他方式可以在 store 裡面存取另一個 store 的 function 跟 state 方法,但是不能拿到即時更新的資料https://github.com/pmndrs/zustand/issues/494, https://github.com/pmndrs/zustand/discussions/630)

Market (Context)->Market (Context):marketCtx.init() {\nworkerCtx.requestHandler("currencies")\nworkerCtx.requestHandler("tickers")\nworkerCtx.requestHandler("guaranteed-stop-fee")\n}
Worker (Context)<--Market (Context):requestHandler() {\n createJob()\n apiWorker.onmessage()\n}
activate Market (Context)
activate Worker (Context)
Web Worker Instance-->Worker (Context):apiWorker.onmessage(result)
deactivate Web Worker Instance
Worker (Context)-->Market (Context):result

deactivate Market (Context)
deactivate Market (Context)

deactivate App (Context)
deactivate Worker (Context)

activate Web Worker Instance

activate Market (Context)

Market (Context)->Market (Context):marketCtx.selectTickerHandler() {\nworkerCtx.requestHandler("trades")\nworkerCtx.requestHandler("ticker-static")\nworkerCtx.requestHandler("ticker-live-statistics")\n}\n

Web Worker Instance<-Worker (Context):apiWorker.postMessage(request)

Worker (Context)<--Market (Context):requestHandler() {\n createJob()\n apiWorker()\n}
activate Market (Context)
activate Worker (Context)
Web Worker Instance-->Worker (Context):apiWorker.onmessage(result)
deactivate Web Worker Instance

Worker (Context)-->Market (Context):result
deactivate Market (Context)

deactivate Market (Context)
deactivate Worker (Context)

Backend->Pusher Instance:send("Trades")
Pusher Instance->Worker (Context):pusher.emit("Trades")
activate Pusher Instance

activate Worker (Context)
Market (Context)->Market (Context):setInterval(() => {\nconvertTradesToCandlesticks(); \nsetCandlestickData();\n}, 100)

Market (Context)->Candlestick Chart Component:marketCtx.candlestickChartData\n(every 0.1 secs)
activate Market (Context)
Candlestick Chart Component->Candlestick Chart Component:drawChart()
deactivate Market (Context)
Worker (Context)->Worker (Context):pusher.on("Trades", event.emit("Trades"))
deactivate Pusher Instance

Worker (Context)->Market (Context):event.emit("Trades")\n\n
deactivate Worker (Context)

activate Market (Context)
Market (Context)->Market (Context):event.on("Trades", addTrades())\n
deactivate Market (Context)
arealclimber commented 9 months ago

taking 3 hrs remaining 3 hrs

arealclimber commented 9 months ago

The primary use case is a single store. Multiple stores make sense if they are totally isolated. It sounds like for your use case, a single store would be a good pattern.

不推薦 store 存取其他的 store 的值,因為拿不到即時的資料,但是調用別人 store 的 function 可以實驗看看 ref

Zustand Store 是彼此平行的,目前我想到的解決方案有:

方法一:用 store 將 worker 功能包起來,類似之前 worker_context

方法二:實作 worker (Singleton) ,讓需要調用 API 的 store 直接使用 worker instance

arealclimber commented 9 months ago
  1. 成功註冊 Pusher (WebSocket)
  2. 初始化 API worker 成功、調用 API 成功拿到最新資料
  3. pusher 推送新的 trades 資料後,context 紀錄新的 trades,然後在每 0.1 秒更新 candlestick chart (待修改)

Untitled (1)

title TBD Frontend Original Pattern (e.g. Candlestick chart)

participant Browser
participant App (Context)
participant Worker (Context)
participant Market (Context)
participant Candlestick Chart Component

participant Web Worker Class

participant Pusher Class

participant Backend

Browser->App (Context):appCtx.init()
activate App (Context)
activate Browser
App (Context)->Worker (Context):workerCtx.init()
activate Worker (Context)
Worker (Context)->Web Worker Class:new Worker(lib)\n
activate Web Worker Class
Worker (Context)<--Web Worker Class:
deactivate Web Worker Class
Pusher Class<-Worker (Context):new Pusher(config)
activate Pusher Class
Worker (Context)<--Pusher Class:
deactivate Pusher Class

App (Context)<--Worker (Context):
deactivate Worker (Context)

App (Context)->Market (Context):marketCtx.init()
activate Market (Context)
group marketCtx.init("currencies","tickers","guaranteed-stop-fee")
Worker (Context)<--Market (Context):requestHandler() {\n createJob()\n apiWorker.onmessage()\n}
activate Worker (Context)
Web Worker Class<-Worker (Context):apiWorker.postMessage(request)
activate Web Worker Class
Web Worker Class-->Worker (Context):apiWorker.onmessage(result)
Worker (Context)->Market (Context):result
end

Market (Context)->Market (Context):marketCtx.selectTickerHandler() {\nworkerCtx.requestHandler("trades")\nworkerCtx.requestHandler("ticker-static")\nworkerCtx.requestHandler("ticker-live-statistics")\n}\n
Worker (Context)<--Market (Context):requestHandler() {\n createJob()\n apiWorker()\n}
Web Worker Class<-Worker (Context):apiWorker.postMessage(request)
Web Worker Class-->Worker (Context):apiWorker.onmessage(result)
deactivate Web Worker Class
Worker (Context)->Market (Context):result
deactivate Worker (Context)
App (Context)<--Market (Context):

deactivate Market (Context)
Browser<--App (Context):
deactivate App (Context)

Browser->Market (Context):render()
activate Market (Context)
Market (Context)->Candlestick Chart Component:marketCtx.candlestickChartData
activate Candlestick Chart Component
Candlestick Chart Component->Candlestick Chart Component:drawChart()
Market (Context)<--Candlestick Chart Component:
deactivate Candlestick Chart Component

Browser<--Market (Context):
deactivate Browser
deactivate Market (Context)

Backend->Pusher Class:send("Trades")
Pusher Class->Pusher Class:pusher.emit("Trades")\n
Pusher Class->Worker (Context):broadcast("Trades")

Pusher Class<--Worker (Context):
Worker (Context)->Worker (Context):pusher.on("Trades", event.emit("Trades"))
Worker (Context)->Market (Context):broadcast("Trades")\n\n
Market (Context)->Market (Context):event.on("Trades", addTrades())\n
Market (Context)->Market (Context):setInterval(() => {\nconvertTradesToCandlesticks(); \nsetCandlestickData();\n}, 100)

Market (Context)->Candlestick Chart Component:marketCtx.candlestickChartData\n(every 0.1 secs)

Candlestick Chart Component->Candlestick Chart Component:drawChart()

Browser<--Candlestick Chart Component:
arealclimber commented 9 months ago

taking 7 hrs https://github.com/CAFECA-IO/TideBit-DeFi/pull/1450

arealclimber commented 9 months ago

透過 #1451 #1454 #1457 ,將 context 依照更新頻率從 market context 另外分出 ticker context 跟 candlestick context,以及 context 之間透過 event 溝通 ,就能避免重複渲染不必要的元件,用 Zustand 重構需要的時間與其能達成的效益不符,雖然能使程式碼更簡潔、增加可讀性,但 TideBit DeFi 需要平行溝通的 context 不多,所以先不需要用 Zustand 重構。