Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Node-AI のリッチな WEB フロントエンドを支える技術 NTT Tech Conference 2024/03/11 何縫ねの。
Slide 2
Slide 2 text
自己紹介 1 • 所属: NTTコミュニケーションズ イノベーションセンター • 趣味: C#, OSS, ドール, 一眼(α7 IV), 映画館 • 執心領域 • C# ⇔ TypeScript • SignalR 何縫ねの。 nenoNaninu nenoMake ブログ https://blog.neno.dev その他 https://neno.dev
Slide 3
Slide 3 text
OSS 紹介 2 属性を付与するだけ Tapper • C# の型定義から TypeScript の型定義を生成する .NET Tool/ library • JSON / MessagePack 対応! https://github.com/nenoNaninu/Tapper
Slide 4
Slide 4 text
OSS 紹介 3 • C# の SignalR Client を強く型付けするための Source Generator TypedSignalR.Client Before After (using TypedSignalR.Client) こんな SignalR の Hub と Receiver の interface が あったとして… 脱文字列! 全てが強く型付け! https://github.com/nenoNaninu/TypedSignalR.Client
Slide 5
Slide 5 text
4 • TypeScript の SignalR Client を強く型付けするための .NET Tool / library TypedSignalR.Client.TypeScript Before After (using TypedSignalR.Client.TypeScript) 脱文字列! 全てが強く型付け! TypeScript 用の型を C# から自動生成 MessagePack Hub Protocol 対応! https://github.com/nenoNaninu/TypedSignalR.Client.TypeScript 属性を付与するだけ! OSS 紹介
Slide 6
Slide 6 text
5 • SignalR 使ったアプリを快適に開発するための GUI を自動生成する library • 2 step で利用可能! • http pipeline に middleware の追加 • Hub と Receiver を定義してる interface に属性を付与 • JWT 認証 サポート • パラメータのユーザ定義型サポート • JSON で入力! SignalR 版 SwaggerUI TypedSignalR.Client.DevTools https://github.com/nenoNaninu/TypedSignalR.Client.DevTools OSS 紹介
Slide 7
Slide 7 text
AspNetCore.SignalR.OpenTelemetry OSS 紹介 6 • トレースのための計装 • 最低限のログ • 接続時 • Transport 層の情報も出力(WebSocket 等) • メソッド呼び出し時 • HubName.MethodName の素朴なログ • メソッド呼び出し毎にログのスコープを追加 • HubName, MethodName, InvocationId を 振っているのでログの検索性が向上 • Duration • 切断時 • 切断時に例外が発生していれば例外もログに出力 Inspired by HttpLogging SignalR のメソッド呼び出し毎に スパンが切られるように https://github.com/nenoNaninu/AspNetCore.SignalR.OpenTelemetry
Slide 8
Slide 8 text
Node-AI とは 7
Slide 9
Slide 9 text
Node-AI とは 8 時系列データの分析に特化した、ノーコードツール (WEB アプリ) https://nodeai.io/ β版公開中!
Slide 10
Slide 10 text
Node-AI とは 9 • NTT Com の Engineers’ Blog にいろいろ書いています • https://engineers.ntt.com/entry/2024/02/19/055601 フロントエンドを Vue.js 2.x から React にリプレイスしました。
Slide 11
Slide 11 text
キャンバスの画的な表現 10
Slide 12
Slide 12 text
キャンバスの画的な表現 11 カードやテキストボックス等が 配置されている領域を「キャンバス」といいます https://nodeai.io/
Slide 13
Slide 13 text
キャンバスの画的な表現 12 • 普通の(?) HTML •
とか • Canvas element () • SVG element () • Scalable Vector Graphics どう表現するか?
Slide 14
Slide 14 text
キャンバスの画的な表現 13 • 普通の(?) HTML •
とか • Canvas element () • SVG element () • Scalable Vector Graphics どう表現するか? 結論: SVG を選択
Slide 15
Slide 15 text
キャンバスの画的な表現 14 • 普通の(?) HTML •
とか • Canvas element () • SVG element () • Scalable Vector Graphics どう表現するか? 結論: SVG を選択 なぜ?
Slide 16
Slide 16 text
キャンバスの画的な表現 15 ① キャンバス内に表示されるオブジェクト(カード等)の実装方法 • スタイリング/イベント/状態の取り扱いまで含めて、どう作るか。 ② 座標系の取り扱い 大事にしたかった事
Slide 17
Slide 17 text
キャンバスの画的な表現 16 キャンバス内に表示される要素(カード等)の実装方法 ① 普通の WEB アプリ同様に実装可能なパターン (HTML / SVG) • SVG の子要素は普通に React で構築できる • SVG には という要素があり、 の子要素は普通に HTML を記述できる • そのため、スタイリング/イベント/状態の取り回しに特別な事が不要 ② 別系統の実装が必要なパターン (Canvas element) • スタイリング/状態/イベントの取り扱いが完全に別系統 • 素の React で GUI を表現する事は不可能
Slide 18
Slide 18 text
キャンバスの画的な表現 17 座標系の取り扱い ① 座標系の権を握れるパターン (SVG / Canvas element) • DOM の座標系と世界座標系の分離 • 世界座標系上のどこの領域を表示するかの制御 ② 座標系の権を握れないパターン (HTML) • DOM の座標系と世界座標系の分離が困難で自由が効かない • 座標・表示領域にまつわる事ほぼ全て CSS でゴリ押し • 適切な座標系の取り扱いが何かと複雑になりがちで辛い
Slide 19
Slide 19 text
キャンバスの画的な表現 18 座標系の取り扱い ① 座標系の権を握れるパターン (SVG / Canvas element) • DOM の座標系と世界座標系の分離 • 世界座標系上のどこの領域を表示するかの制御 ② 座標系の権を握れないパターン (HTML) • DOM の座標系と世界座標系の分離が困難で自由が効かない • 座標・表示領域にまつわる事ほぼ全て CSS でゴリ押し • 適切な座標系の取り扱いが何かと複雑になりがちで辛い 世界座標系 : キャンバス上のオブジェクト(カード等)が 配置されている座標系
Slide 20
Slide 20 text
キャンバスの画的な表現 19 座標系の取り扱い ① 座標系の権を握れるパターン (SVG / Canvas element) • DOM の座標系と世界座標系の分離 • 世界座標系上のどこの領域を表示するかの制御 ② 座標系の権を握れないパターン (HTML) • DOM の座標系と世界座標系の分離が困難で自由が効かない • 座標・表示領域にまつわる事ほぼ全て CSS でゴリ押し • 適切な座標系の取り扱いが何かと複雑になりがちで辛い 世界座標系 : キャンバス上のオブジェクト(カード等)が 配置されている座標系 キャンバスの 拡大縮小や ミニマップで表示領域を 可視化するために必須
Slide 21
Slide 21 text
キャンバスの画的な表現 20 座標系の取り扱い ① 座標系の権を握れるパターン (SVG / Canvas element) • DOM の座標系と世界座標系の分離 • 世界座標系上のどこの領域を表示するかの制御 ② 座標系の権を握れないパターン (HTML) • DOM の座標系と世界座標系の分離が困難で自由が効かない • 座標・表示領域にまつわる事ほぼ全て CSS でゴリ押し • 適切な座標系の取り扱いが何かと複雑になりがちで辛い 世界座標系 : キャンバス上のオブジェクト(カード等)が 配置されている座標系 キャンバスの 拡大縮小や ミニマップで表示領域を 可視化するために必須 ブラウザ/ライブラリに任せると 座標系の権は握れない
Slide 22
Slide 22 text
キャンバスの画的な表現 21 SVG のメリット • 標準的な WEB アプリ開発の知識がそのまま使える • DOM の座標系と世界座標系の分離できる • キャンバス上のオブジェクトの表示領域の制御がしやすい
Slide 23
Slide 23 text
キャンバスの画的な表現 22 SVG のメリット • 標準的な WEB アプリ開発の知識がそのまま使える • DOM の座標系と世界座標系の分離できる • キャンバス上のオブジェクトの表示領域の制御がしやすい 具体的にどのように実装するか?
Slide 24
Slide 24 text
キャンバスの画的な表現 23 SVG の API
Slide 25
Slide 25 text
キャンバスの画的な表現 24 SVG の API viewBox で 表示領域を制御
Slide 26
Slide 26 text
キャンバスの画的な表現 25 SVG の API viewBox で 表示領域を制御
Slide 27
Slide 27 text
キャンバスの画的な表現 26 SVG の API viewBox で 表示領域を制御
Slide 28
Slide 28 text
キャンバスの画的な表現 27 SVG の API svg の子要素に React コンポーネントの配置可能 viewBox で 表示領域を制御
Slide 29
Slide 29 text
キャンバスの画的な表現 28 SVG の API transform で 座標を簡単に制御可能 svg の子要素に React コンポーネントの配置可能 viewBox で 表示領域を制御
Slide 30
Slide 30 text
キャンバスの画的な表現 29 SVG の API transform で 座標を簡単に制御可能 foreignObject 内では 通常の HTML を記述可能 svg の子要素に React コンポーネントの配置可能 viewBox で 表示領域を制御
Slide 31
Slide 31 text
キャンバスの画的な表現 30 SVG の API 画としての表現力は十分 座標系も考えやすい 実装もしやすい transform で 座標を簡単に制御可能 foreignObject 内では 通常の HTML を記述可能 svg の子要素に React コンポーネントの配置可能 viewBox で 表示領域を制御
Slide 32
Slide 32 text
キャンバスの画的な表現 31 ViewBox を素で扱うのは少々難儀 ① ViewBox の適切な制御を都度考えるのは難しい • e.g., 右下隅に最大までzoom in してから zoom outした場合の表示領域は… ② スクリーン(viewport)座標系と世界座標系の対応させる必要あり • ViewBox の値に次第で異なるので色々計算する必要あり ③ スクリーンサイズの変化に応じた ViewBox に制御 • 要するにブラウザのウィンドウサイズの変化 世界座標系は結果的に SVG 上での座標系と対応
Slide 33
Slide 33 text
キャンバスの画的な表現 32 カメラという概念を導入 • 開発者に ViewBox を意識させない。 • 開発者が意識するのは カメラの位置をどう動かすか。 • 細かい適切な制御はカメラの 内部実装で隠蔽。 • 座標変換系のメソッドも用意。 開発者はこれらのメソッド使えば 深い事気にする必要なし。 • ゲームエンジンっぽい API。 馴染みがある人はすんなり 理解できる。 これくらい実装すれば座標系を完全に掌握できる。 ライブラリ不要。
Slide 34
Slide 34 text
リアルタイム共同編集 33
Slide 35
Slide 35 text
リアルタイム共同編集 34 Node-AI はデータ分析業務のコラボレーションを推進しています • 1つのキャンバスに複数人で参加する • 分析者同士がいっしょに議論をしながら作業 • ドメイン知識を持っている人やステークホルダーを巻き込んで分析
Slide 36
Slide 36 text
リアルタイム共同編集 35 Node-AI はデータ分析業務のコラボレーションを推進しています • 1つのキャンバスに複数人で参加する • 分析者同士がいっしょに議論をしながら作業 • ドメイン知識を持っている人やステークホルダーを巻き込んで分析 リプレイス前は1つのキャンバスを 同時に複数人が操作する ユースケースに対応できていなかった
Slide 37
Slide 37 text
リアルタイム共同編集 36 Node-AI はデータ分析業務のコラボレーションを推進しています • 1つのキャンバスに複数人で参加する • 分析者同士がいっしょに議論をしながら作業 • ドメイン知識を持っている人やステークホルダーを巻き込んで分析 リプレイス前は1つのキャンバスを 同時に複数人が操作する ユースケースに対応できていなかった ブラウザで更新かけないと 他人の操作内容が表示されなかった
Slide 38
Slide 38 text
リアルタイム共同編集 37 • リアルタイム共同編集 • Undo/Redo リプレイスを機に実現したい事
Slide 39
Slide 39 text
リアルタイム共同編集 38 • リアルタイム共同編集 • Undo/Redo リプレイスを機に実現したい事 後から実現するのはめちゃ大変
Slide 40
Slide 40 text
• SignalR: リアルタイムの双方向 RPC library リアルタイム共同編集 39 リアルタイム通信は SignalR Front-end (TypeScript / React) Back-end (C# / ASP.NET Core) SignalR
Slide 41
Slide 41 text
• SignalR: リアルタイムの双方向 RPC library リアルタイム共同編集 40 リアルタイム通信は SignalR Front-end (TypeScript / React) Back-end (C# / ASP.NET Core) SignalR WebSocket / SSE / Long Polling
Slide 42
Slide 42 text
• SignalR: リアルタイムの双方向 RPC library リアルタイム共同編集 41 リアルタイム通信は SignalR Front-end (TypeScript / React) Back-end (C# / ASP.NET Core) SignalR Tapper TypedSignalR.Client.TypeScript WebSocket / SSE / Long Polling
Slide 43
Slide 43 text
• SignalR: リアルタイムの双方向 RPC library リアルタイム共同編集 42 リアルタイム通信は SignalR Front-end (TypeScript / React) Back-end (C# / ASP.NET Core) SignalR Tapper TypedSignalR.Client.TypeScript TypeScript 側は こんな感じで記述 (C# からコードが生成される) WebSocket / SSE / Long Polling
Slide 44
Slide 44 text
• SignalR: リアルタイムの双方向 RPC library リアルタイム共同編集 43 リアルタイム通信は SignalR Front-end (TypeScript / React) Back-end (C# / ASP.NET Core) SignalR Tapper TypedSignalR.Client.TypeScript TypedSignalR.Client TypedSignalR.Client.DevTools AspNetCore.SignalR.OpenTelemetry WebSocket / SSE / Long Polling TypeScript 側は こんな感じで記述 (C# からコードが生成される)
Slide 45
Slide 45 text
• キャンバス毎にコネクションをグルーピング リアルタイム共同編集 44 リアルタイム通信は SignalR Client SignalR Hub (back-end) Client Client 同一のキャンバスに複数人 参加している場合
Slide 46
Slide 46 text
• キャンバス毎にコネクションをグルーピング リアルタイム共同編集 45 リアルタイム通信は SignalR Client SignalR Hub (back-end) Client Client 同一のキャンバスに複数人 参加している場合
Slide 47
Slide 47 text
• キャンバス毎にコネクションをグルーピング リアルタイム共同編集 46 リアルタイム通信は SignalR Client SignalR Hub (back-end) Client Client broadcast 同一のキャンバスに複数人 参加している場合
Slide 48
Slide 48 text
• キャンバス毎にコネクションをグルーピング リアルタイム共同編集 47 リアルタイム通信は SignalR Client SignalR Hub (back-end) Client Client broadcast 同一のキャンバスに複数人 参加している場合
Slide 49
Slide 49 text
リアルタイム共同編集 48 • Command パターンは「命令」を「オブジェクト」にするパターン • Undo/Redo は操作履歴を stack に積む必要があるのでこのパターンが必須 Undo/Redo に必要なのは Command パターン
Slide 50
Slide 50 text
リアルタイム共同編集 49 • Command パターンは「命令」を「オブジェクト」にするパターン • Undo/Redo は操作履歴を stack に積む必要があるのでこのパターンが必須 Undo/Redo に必要なのは Command パターン
Slide 51
Slide 51 text
リアルタイム共同編集 50 • Command パターンは「命令」を「オブジェクト」にするパターン • Undo/Redo は操作履歴を stack に積む必要があるのでこのパターンが必須 Undo/Redo に必要なのは Command パターン 典型的な Command パターンは React に不適切
Slide 52
Slide 52 text
リアルタイム共同編集 51 • Command パターンは「命令」を「オブジェクト」にするパターン • Undo/Redo は操作履歴を stack に積む必要があるのでこのパターンが必須 Undo/Redo に必要なのは Command パターン 典型的な Command パターンは React に不適切 この interface を実装した class に 変更対象のオブジェクトの 参照を握らせ、 execute() 実行時に副作用を起こし 状態/描画に影響を及ぼす
Slide 53
Slide 53 text
リアルタイム共同編集 52 • Command パターンは「命令」を「オブジェクト」にするパターン • Undo/Redo は操作履歴を stack に積む必要があるのでこのパターンが必須 Undo/Redo に必要なのは Command パターン 典型的な Command パターンは React に不適切 この interface を実装した class に 変更対象のオブジェクトの 参照を握らせ、 execute() 実行時に副作用を起こし 状態/描画に影響を及ぼす React の思想/実装 双方の面で噛み合わない
Slide 54
Slide 54 text
リアルタイム共同編集 53 どうすれば React に うまく落とし込めるか?
Slide 55
Slide 55 text
リアルタイム共同編集 54 どうすれば React に うまく落とし込めるか? 前提: キャンバスの状態管理に Redux を用います
Slide 56
Slide 56 text
リアルタイム共同編集 55 React に適した パターンを考える
Slide 57
Slide 57 text
リアルタイム共同編集 56 React に適した パターンを考える 基本は command パターン。 これを分解し React に適合させつつ 自分たちのアプリに 適切な設計に落とし込む
Slide 58
Slide 58 text
リアルタイム共同編集 57 React に適した パターンを考える Message (データ) Service (処理) 基本は command パターン。 これを分解し React に適合させつつ 自分たちのアプリに 適切な設計に落とし込む
Slide 59
Slide 59 text
リアルタイム共同編集 58 React に適した パターンを考える Message (データ) Service (処理) 基本は command パターン。 これを分解し React に適合させつつ 自分たちのアプリに 適切な設計に落とし込む React は関数型を指向している 親和性 UP!
Slide 60
Slide 60 text
リアルタイム共同編集 59 React に適した パターンを考える Message (データ) Service (処理) 各 command 毎に存在する 既存の状態を受け取り 新しい状態を返す各副作用の無い service ① command と stack の管理 ② 適切な CommandExecutor の呼び出し 基本は command パターン。 これを分解し React に適合させつつ 自分たちのアプリに 適切な設計に落とし込む CommandExecutor CommandRunner React は関数型を指向している 親和性 UP!
Slide 61
Slide 61 text
リアルタイム共同編集 60 型としては概ねこんな感じ
Slide 62
Slide 62 text
リアルタイム共同編集 61 型としては概ねこんな感じ Commnad を stack するしない
Slide 63
Slide 63 text
リアルタイム共同編集 62 型としては概ねこんな感じ Commnad を stack するしない 操作手段が 追加されるたび メンバが追加される
Slide 64
Slide 64 text
リアルタイム共同編集 63 先ほどの型の使い方 1つの OperationType に 1つの Command 1つの Command に 1つの CommandExecutor
Slide 65
Slide 65 text
リアルタイム共同編集 64 Command を dispatch する際のコード 例えばカード配置時の Callback はこんな感じ Reducer のコード
Slide 66
Slide 66 text
リアルタイム共同編集 65 Command を dispatch する際のコード 例えばカード配置時の Callback はこんな感じ Reducer は同期メソッドなので 非同期処理を済ませてから dispatch する Reducer のコード
Slide 67
Slide 67 text
リアルタイム共同編集 66 Command を dispatch する際のコード 例えばカード配置時の Callback はこんな感じ Reducer は同期メソッドなので 非同期処理を済ませてから dispatch する Reducer のコード CommandRunner.run() の中では ① Stack の操作 ② 対象の command に適した CommandExecutor の実行
Slide 68
Slide 68 text
リアルタイム共同編集 67 Undo/Redo するコード
Slide 69
Slide 69 text
リアルタイム共同編集 68 Undo/Redo するコード CommandRunner.undoAsync() の中では ① Stack の操作 (同期) ② 対象の command に適した SignalR の RPC 呼び出し(非同期) ③ 対象の command に適した CommandExecutor の実行 (同期)
Slide 70
Slide 70 text
リアルタイム共同編集 69 Undo/Redo するコード Undo/Redo は非同期が生じる。 Redux の世界で非同期を実行するには Redux Thunk や redux-saga を導入する必要性ある。 それは実現したい事に対してヘビー。 CommandRunner.undoAsync() の中では ① Stack の操作 (同期) ② 対象の command に適した SignalR の RPC 呼び出し(非同期) ③ 対象の command に適した CommandExecutor の実行 (同期)
Slide 71
Slide 71 text
リアルタイム共同編集 70 Undo/Redo するコード Dispatch する前に非同期処理を 実行しておく CommandRunner.undoAsync() の中では ① Stack の操作 (同期) ② 対象の command に適した SignalR の RPC 呼び出し(非同期) ③ 対象の command に適した CommandExecutor の実行 (同期) Undo/Redo は非同期が生じる。 Redux の世界で非同期を実行するには Redux Thunk や redux-saga を導入する必要性ある。 それは実現したい事に対してヘビー。
Slide 72
Slide 72 text
リアルタイム共同編集 71 実はリアルタイム共同編集に対応する場合も違いはこれだけ…! 他ユーザの操作 自分の操作
Slide 73
Slide 73 text
リアルタイム共同編集 72 実はリアルタイム共同編集に対応する場合も違いはこれだけ…! 他ユーザの操作 CommandType を変えればいいだけ 自分の操作
Slide 74
Slide 74 text
リアルタイム共同編集 73 実はリアルタイム共同編集に対応する場合も違いはこれだけ…! 他ユーザの操作 CommandType を変えればいいだけ 自分の操作 自分だろうと他人だろうと 同一の操作に対する処理は 同一の CommandExecutor で処理する (共通化)
Slide 75
Slide 75 text
リアルタイム共同編集 74 機能追加は簡単でなければいけない ① OperationType (enum)にメンバを追加し ② 追加した OperationType に対応する Command を作成し ③ 追加した Command に対応する CommandExecutor を実装し ④ CommandExecutor を配列に追加する
Slide 76
Slide 76 text
リアルタイム共同編集 75 機能追加は簡単でなければいけない ① OperationType (enum)にメンバを追加し ② 追加した OperationType に対応する Command を作成し ③ 追加した Command に対応する CommandExecutor を実装し ④ CommandExecutor を配列に追加する Stack の操作等は 開発者に意識させない
Slide 77
Slide 77 text
リアルタイム共同編集 76 機能追加は簡単でなければいけない ① OperationType (enum)にメンバを追加し ② 追加した OperationType に対応する Command を作成し ③ 追加した Command に対応する CommandExecutor を実装し ④ CommandExecutor を配列に追加する OperationType で switch とかは書かない Stack の操作等は 開発者に意識させない
Slide 78
Slide 78 text
Node-AI は時系列データの分析に特化したノーコードツール • β版公開中!是非使ってね! https://nodeai.io/ キャンバスの画的な表現 • SVG を活用 • カメラという抽象 リアルタイム共同編集 • リアルタイム通信は SignalR • リアルタイム共同編集 + Undo/Redo のための command パターンベースの設計 まとめ 77 提示したコードや設計は あくまで「エッセンス」です