Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
kondei
twitter: @_kondei
Clean Architectureで設計して
RxJSを使った話
何に使ったの?
ニコニコ生放送の、動画広告
…を配信するための、運営ツール
(ユーザーには見えない)
素のTypeScriptで実装するか…
▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂ うわああああああ
要件が複雑
• 機能が多い
• 13個の細かい機能
• 3つのAPIを叩く
• 状態が多い
• 非同期処理が多く、イベント発火の時系列が複雑
• ツールの有効無効の切り替え
• カウントダウン表示 & 終了時に色んな処理
• ツールをクリックし...
• 非同期処理のリッチなライブラリを使う
• アーキテクチャは何でもいいが、「プレゼンテー
ションとドメインの分離」原則を適用する
• データの流れと依存関係を単方向にする
• Fluxや、ちゃんとしたMVCのように
• 可能なかぎりReact...
何を採用しよう?
RxJS 採用
• jQueryと協調する
• TypeScriptですぐに使えるFRPライブラリ
• IE8対応してる
• 社内で使用実績があった(nicocas)
• (のちのち書いたFRPライブラリ比較)
• http://qiita.c...
クリーンアーキテクチャ 採用
• 厳密に適用すれば、依存関係は一方向になる
• DDDを適用したほうが機能拡張に強いはず
• サーバサイドはDDDだし…
http://blog.8thlight.com/uncle-bob/2012/08/13...
設計
ControllerGateway Presenter
Domain Model
Usecase
View (html)
API
<I> Gateway
<I> Controller
External
Interface
Interface
A...
ControllerGateway Presenter
Domain Model
Usecase
View (html)
API
<I> Gateway
<I> Controller
External
Interface
Interface
A...
ControllerGateway Presenter
Domain Model
Usecase
View (html)
API
<I> Gateway
<I> Controller
External
Interface
Interface
A...
ControllerGateway Presenter
Domain Model
Usecase
View (html)
API
<I> Gateway
<I> Controller
External
Interface
Interface
A...
Presenter は何をしてるの
1. Controller や Usecase らの Observable を入力
2. ストリーム(イベント)を操作・合成
3. Subscribe 内で DOM 操作
表示要素が、ユースケースにReact...
どういう流れでストリーム構築するの
1. TypeScriptだったので、全てconstructorで構築
要するに、ストリームを1度だけ宣言することが重要
2. 後述のInitializerで、依存関係の無い順に初期化
こんなInitializerをHTMLから呼んだ
export class Initializer {
constructor(private apiUrl: string,
private $hogeElement: JQuery) {
//...
実装後しばらくして気づいた
MVI アーキテクチャに似てた
Model
Intent View
入力: Modelからのデータイベント
責務: プレゼンテーションロジック
出力: Modelのレンダリングや、生
のユーザー入力イベント
入力: Intentからのユーザーイ...
MVI とは
• 完全リアクティブなMVC
• 提唱者によるフレームワークがある
• staltz/Cycle.js
• RxJSを活用している
• Flux/Reactで使ってるEvent emitterは単機能すぎるという意見らしい
htt...
クリーンアーキテクチャ 感想
今回の自分の設計では…
• RxJS とうまく協調できた
• MVIに近いものになり、その方が単純化できただろう
• 設計力不足でドメインモデル貧血症になった
• フロントエンドでは、動画広告というドメインモデルが...
心残り
jQueryでDOM操作した部分がInteractive感ある
• MVVM のようなデータバインドでReactiveにできそう
• http://webrxjs.org/ というRxJSのMVVMフレームワークもある
• Cycle....
RxJS だけの話
RxJS 気をつけたこと
• なるべくストリームの操作で済ませる
• Subscribe を書く場所を絞る(今回は基本的にPresenter)
• Subscribe 内で Subscribe しない
• 副作用はSubscribeに書き、Do...
RxJS 気をつけたこと
• Subjectをなるべく使わない。使える用途は、
• テスト用
• 非Observable → Observableのラッパ作るとき
• 何らかの事情でObservableを拡張したいとき
• (とはいえpubli...
RxJS 気をつけたこと
• OnNextを避ける
• 明示的に書くと手続き型になるため
• 手続き型に書いてるなら使ってもいいかも(ただしその場合
はクラス内に隠蔽し、外に公開するならasObservableをかま
せる)
RxJS 気をつけたこと
• Observable を公開するときはHot変換しておく
• Coldのまま公開して分岐すると、Subscribe のたびにその
Observable内の処理が走る
RxJS 気をつけたこと
• 知見の共有!
• 使い手が少ないとレビュー依頼にも困る
• slackの #rx 活用
• 社内ConfluenceにRxの知見まとめページを作った
• 他、Qiitaに記事を上げたりなど
RxJS ハマったこと
• 現象
• APIリクエストを含むストリームを関数で定義して公開した
ら、Subscribeの回数分APIリクエストが走った
• 理由
• Subscribeのたびに関数が呼ばれ、ストリームが構築され、
APIリクエス...
RxJS ハマったこと
• 現象
• Cold Observableを Hot変換してrepeat()したら、Cold
Observable の終了後に Call stack overflow
• 理由
• 終了した Cold が Comple...
RxJS ハマったこと
• 現象
• just(jQuery.val()) を返す関数を作ったら、undefined が
流れてきた
• 理由
• just でストリーム作るタイミングで jQuery.val()が
undefined だった
...
RxJS ハマったこと
• 現象
• 定期的な fromPromise を起点にした処理が、IEでは2度目の
リクエスト以降動作しなかった
• 理由
• IE は Ajax の GET をキャッシュするせいで、2度目以降は
ストリームが発火しな...
RxJS ハマったこと
• 現象
• IE8でエラー
• 理由
• IE8の予約語に「do」があり、do()と重複
• 対策
• RxJSのGitHubドキュメント見て、別名メソッドに変える
• do → tap
• catch → catch...
RxJS 使って何が良かった?
• 複雑な処理を、他の複数の処理の起点にできた
• Observableの公開・合成 最高に便利!
• 素早く機能追加できた
• ストリームに関数1個挟むだけだったり
• 時間の概念を含む実装が楽だった
• 定期...
RxJS 使わなかったら?
• 独自イベントの乱舞
• コールバック地獄
総括
• RxJS と Clean Architecture を良く協調させれた
• RxJS を全面的に使う際のアーキテクチャは、大抵は
MVIの方が単純で良い
• RxJS は罠が多いので知見を共有すべし
リンク
• クリーンアーキテクチャ 初出記事
• http://blog.8thlight.com/uncle-bob/2012/08/13/the-
clean-architecture.html
• クリーンアーキテクチャについて書いた記事...
Upcoming SlideShare
Loading in …5
×

Clean Architectureで設計してRxJSを使った話

10,254 views

Published on

歌舞伎座.tech#7「Reactive Extensions」での発表資料
http://kbkz.connpass.com/event/12597/

Published in: Engineering
  • Be the first to comment

Clean Architectureで設計してRxJSを使った話

  1. 1. kondei twitter: @_kondei Clean Architectureで設計して RxJSを使った話
  2. 2. 何に使ったの? ニコニコ生放送の、動画広告 …を配信するための、運営ツール (ユーザーには見えない)
  3. 3. 素のTypeScriptで実装するか…
  4. 4. ▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂ うわああああああ
  5. 5. 要件が複雑 • 機能が多い • 13個の細かい機能 • 3つのAPIを叩く • 状態が多い • 非同期処理が多く、イベント発火の時系列が複雑 • ツールの有効無効の切り替え • カウントダウン表示 & 終了時に色んな処理 • ツールをクリックしたら10秒おきに動画広告状態取得開始 • 色んな表示を10秒おきに取る動画広告状態に同期 • …
  6. 6. • 非同期処理のリッチなライブラリを使う • アーキテクチャは何でもいいが、「プレゼンテー ションとドメインの分離」原則を適用する • データの流れと依存関係を単方向にする • Fluxや、ちゃんとしたMVCのように • 可能なかぎりReactiveに書く • Interactiveに操作しだすと、どこから変更されるのか分か らない こうしないと書ける気がしない
  7. 7. 何を採用しよう?
  8. 8. RxJS 採用 • jQueryと協調する • TypeScriptですぐに使えるFRPライブラリ • IE8対応してる • 社内で使用実績があった(nicocas) • (のちのち書いたFRPライブラリ比較) • http://qiita.com/kondei/items/17e5d4867a0652911e52
  9. 9. クリーンアーキテクチャ 採用 • 厳密に適用すれば、依存関係は一方向になる • DDDを適用したほうが機能拡張に強いはず • サーバサイドはDDDだし… http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
  10. 10. 設計
  11. 11. ControllerGateway Presenter Domain Model Usecase View (html) API <I> Gateway <I> Controller External Interface Interface Adapters Application Business Rules Enterprise Business Rules
  12. 12. ControllerGateway Presenter Domain Model Usecase View (html) API <I> Gateway <I> Controller External Interface Interface Adapters Application Business Rules Enterprise Business Rules 入力イベントをObservable として公開 APIレスポンスを Observableとして公開
  13. 13. ControllerGateway Presenter Domain Model Usecase View (html) API <I> Gateway <I> Controller External Interface Interface Adapters Application Business Rules Enterprise Business Rules 入力イベントをObservable として公開 APIレスポンスを Observableとして公開 Domain Modelや入出力を利用して ユース ケースのドメインロジックを実現し、 Observableとして公開
  14. 14. ControllerGateway Presenter Domain Model Usecase View (html) API <I> Gateway <I> Controller External Interface Interface Adapters Application Business Rules Enterprise Business Rules 入力イベントをObservable として公開 APIレスポンスを Observableとして公開 Domain Modelや入出力を利用して ユース ケースのドメインロジックを実現し、 Observableとして公開 UsecaseやControllerの Observableを利用して プレゼンテーションロジックを実現
  15. 15. Presenter は何をしてるの 1. Controller や Usecase らの Observable を入力 2. ストリーム(イベント)を操作・合成 3. Subscribe 内で DOM 操作 表示要素が、ユースケースにReactする
  16. 16. どういう流れでストリーム構築するの 1. TypeScriptだったので、全てconstructorで構築 要するに、ストリームを1度だけ宣言することが重要 2. 後述のInitializerで、依存関係の無い順に初期化
  17. 17. こんなInitializerをHTMLから呼んだ export class Initializer { constructor(private apiUrl: string, private $hogeElement: JQuery) { // 入力 var controller = new Controller($hogeElement); var apiClient = new ApiClient(apiUrl); // ユースケース var usecase1 = new Usecase1(controller, apiClient); var usecase2 = new Usecase2(controller, apiClient); var usecase3 = new Usecase3(controller); // 出力 var presenter1 = new Presenter1(controller, usecase1, …); var presenter2 = new Presenter2(controller, usecase1, …); } }
  18. 18. 実装後しばらくして気づいた
  19. 19. MVI アーキテクチャに似てた Model Intent View 入力: Modelからのデータイベント 責務: プレゼンテーションロジック 出力: Modelのレンダリングや、生 のユーザー入力イベント 入力: Intentからのユーザーインタラクションイベント 責務: ビジネスロジック 出力: データイベント 入力: Viewからの生のユーザー入力イベント 出力: Modelフレンドリーなユーザーインタラ クションイベント(Observable)
  20. 20. MVI とは • 完全リアクティブなMVC • 提唱者によるフレームワークがある • staltz/Cycle.js • RxJSを活用している • Flux/Reactで使ってるEvent emitterは単機能すぎるという意見らしい http://futurice.com/blog/reactive-mvc-and-the-virtual-dom/
  21. 21. クリーンアーキテクチャ 感想 今回の自分の設計では… • RxJS とうまく協調できた • MVIに近いものになり、その方が単純化できただろう • 設計力不足でドメインモデル貧血症になった • フロントエンドでは、動画広告というドメインモデルがフラ グ1個と同一性判定しか持たなかった • 動画広告案件のユビキタス言語も決めたが、ほぼサーバ側で しか使わなかった • この規模で依存関係の逆転は過度の抽象化だった
  22. 22. 心残り jQueryでDOM操作した部分がInteractive感ある • MVVM のようなデータバインドでReactiveにできそう • http://webrxjs.org/ というRxJSのMVVMフレームワークもある • Cycle.jsでJSXで宣言的にDOM操作する例あり • https://github.com/ivan-kleshnin/cyclejs-examples
  23. 23. RxJS だけの話
  24. 24. RxJS 気をつけたこと • なるべくストリームの操作で済ませる • Subscribe を書く場所を絞る(今回は基本的にPresenter) • Subscribe 内で Subscribe しない • 副作用はSubscribeに書き、Doは極力避ける • ストリームの途中で副作用を書くと、分岐したら何の原因で 副作用が発生するかわからなくなる
  25. 25. RxJS 気をつけたこと • Subjectをなるべく使わない。使える用途は、 • テスト用 • 非Observable → Observableのラッパ作るとき • 何らかの事情でObservableを拡張したいとき • (とはいえpublish/multicast等が似たようなことしているので必 要ないかも) • 子ストリームの結果を親へ流したくて、副作用でデータを受 け付けて流すストリームを提供したいとき
  26. 26. RxJS 気をつけたこと • OnNextを避ける • 明示的に書くと手続き型になるため • 手続き型に書いてるなら使ってもいいかも(ただしその場合 はクラス内に隠蔽し、外に公開するならasObservableをかま せる)
  27. 27. RxJS 気をつけたこと • Observable を公開するときはHot変換しておく • Coldのまま公開して分岐すると、Subscribe のたびにその Observable内の処理が走る
  28. 28. RxJS 気をつけたこと • 知見の共有! • 使い手が少ないとレビュー依頼にも困る • slackの #rx 活用 • 社内ConfluenceにRxの知見まとめページを作った • 他、Qiitaに記事を上げたりなど
  29. 29. RxJS ハマったこと • 現象 • APIリクエストを含むストリームを関数で定義して公開した ら、Subscribeの回数分APIリクエストが走った • 理由 • Subscribeのたびに関数が呼ばれ、ストリームが構築され、 APIリクエストも走った • 対策 • ストリームはコンストラクタで定義してローカルメンバ変数 に代入しておき、それを返すだけの関数を提供すべし
  30. 30. RxJS ハマったこと • 現象 • Cold Observableを Hot変換してrepeat()したら、Cold Observable の終了後に Call stack overflow • 理由 • 終了した Cold が Completed を流し、repeat()が再 Subscribeする、を無限に再帰 • 対策 • Cold ObservableをHot変換したらrepeat()しない • Rx の仕様かも? Special Thanks: wilfrem さん、toRisouP さん
  31. 31. RxJS ハマったこと • 現象 • just(jQuery.val()) を返す関数を作ったら、undefined が 流れてきた • 理由 • just でストリーム作るタイミングで jQuery.val()が undefined だった • 対策 • defer(() => just(JQuery.val())) のように、遅延評価
  32. 32. RxJS ハマったこと • 現象 • 定期的な fromPromise を起点にした処理が、IEでは2度目の リクエスト以降動作しなかった • 理由 • IE は Ajax の GET をキャッシュするせいで、2度目以降は ストリームが発火しない • 対策 • cache: false 追加 • http://qiita.com/kondei/items/99648a07418793d5f50b
  33. 33. RxJS ハマったこと • 現象 • IE8でエラー • 理由 • IE8の予約語に「do」があり、do()と重複 • 対策 • RxJSのGitHubドキュメント見て、別名メソッドに変える • do → tap • catch → catchException
  34. 34. RxJS 使って何が良かった? • 複雑な処理を、他の複数の処理の起点にできた • Observableの公開・合成 最高に便利! • 素早く機能追加できた • ストリームに関数1個挟むだけだったり • 時間の概念を含む実装が楽だった • 定期的リクエスト • 複数の終了条件を持つカウントダウンも簡単に private countdownStream(remain: number) { Rx.Observable .timer(0, this.countdownSpan) .map(r => { return remain - r + 1; }).takeWhile(r => { // 0秒で終了 return r >= -1; }).takeUntil(動画広告状態が非再生中); }
  35. 35. RxJS 使わなかったら? • 独自イベントの乱舞 • コールバック地獄
  36. 36. 総括 • RxJS と Clean Architecture を良く協調させれた • RxJS を全面的に使う際のアーキテクチャは、大抵は MVIの方が単純で良い • RxJS は罠が多いので知見を共有すべし
  37. 37. リンク • クリーンアーキテクチャ 初出記事 • http://blog.8thlight.com/uncle-bob/2012/08/13/the- clean-architecture.html • クリーンアーキテクチャについて書いた記事 • http://qiita.com/kondei/items/41c28674c1bfd4156186 • RxJS について書いた記事 • http://qiita.com/kondei/items/99648a07418793d5f50b • http://qiita.com/kondei/items/17e5d4867a0652911e52

×