アナログ金木犀

つれづれなるまままにつれづれする

改めて、SOLID(+ α)

こんにちは、kgmyshinです。

この記事は DMMグループAdvent Calendar の6日目の記事となります。 昨日は弊社新卒ありかくん( ありかくんの記事はこちら )に引き続き本日は自分の担当となります。

何かしらの技術ネタだったりを仕込みたかったのですが、時間がなかったのもありまして、今ちょうど作っているSOLID+α原則研修そのものと一部コンテンツの紹介の場とさせてください。 こういう社内研修をやろうとしてますよ、やってますよという宣伝です。

内容としては、N番どころじゃない煎じですが、それでも世間一般に出回ってるコンテンツよりもわかりやすいものを目指してたりはするのでチラ見してもらえると嬉しいです。

f:id:kgmyshin:20191202185653j:plain

本研修では 座学 -> 個人ワーク -> グループディスカッション という流れで行う予定です。

研修のスコープは「SOLID 原則 + CQS原則 + DRY原則です。 ターゲットは「これらを知りたい方、復習したい方」で、研修のゴールとしてはこれらの原則を理解してもらうことで普段の設計・コーディング・レビュー時の質が上がることとしています。

スライドは全部で130ページほどあり、ここでは一部の紹介となりますが、おいおいスライドの外部公開もするので興味ある方はもう少しお待ちください。

参考図書

参考図書は以下になります。

原則の解釈にオリジナリティが出ては困るので、自分でも改めてSOLID原則の章をいくつかの資料で確認しながら作っています。

日程感

(1.5時間~2時間)×2日間 の合計3~4時間となっています。

  • 1日目
    • SRP(単一責務原則)
    • ISP(インターフェース分離の原則)
    • DIP(依存関係逆転の原則)
  • 2日目
    • OCP(オープンクローズド原則)
    • LSP(リスコフの置換原則)
    • CQS(コマンドクエリ分離の原則)
    • DRY(Don’t Repeat Your Self原則)

SOLIDの順番にはしていません。

f:id:kgmyshin:20191202190546j:plain

コンテンツ覗き見

早速いくつかのコンテンツを覗き見してみましょう。

OCP(オープンクローズド原則)

スライド 説明
f:id:kgmyshin:20191202190037j:plain:w300
f:id:kgmyshin:20191202190111j:plain:w300
f:id:kgmyshin:20191202190318j:plain:w300 まずはよくあるOC原則違反の例を示します。
f:id:kgmyshin:20191202190328j:plain:w300
f:id:kgmyshin:20191202190336j:plain:w300
f:id:kgmyshin:20191202190345j:plain:w300
f:id:kgmyshin:20191202190351j:plain:w300 OC原則を守った場合、修正範囲が新しいクラスだけであることを明示し、この後にどうこのクラス設計を導くのかについて軽く触れます。
ブラッシュアップ中なのでスライドは省きますが、SRPやDIPに従ってクラス設計していくことでOC原則を満たすコードが書きやすくなることを説明します

DRY(Don’t Repeat Your Self原則)

DRY原則は誤解も多いのでその誤解をまず解きつつ、またやりすぎてしまう例にもしっかり触れるようにしました。

スライド 説明
f:id:kgmyshin:20191202193935j:plain:w300
f:id:kgmyshin:20191202193942j:plain:w300 誤解が多いところなのでしっかり触れておきます
f:id:kgmyshin:20191202193957j:plain:w300
f:id:kgmyshin:20191202194007j:plain:w300
f:id:kgmyshin:20191202194015j:plain:w300
f:id:kgmyshin:20191202194022j:plain:w300
f:id:kgmyshin:20191202194040j:plain:w300 コード重複の例にも触れておきます
f:id:kgmyshin:20191202194049j:plain:w300
f:id:kgmyshin:20191202194056j:plain:w300 重要なところです
f:id:kgmyshin:20191202194104j:plain:w300 DRY原則を守りすぎることで辛くなる例に触れます
f:id:kgmyshin:20191202194112j:plain:w300
f:id:kgmyshin:20191202194121j:plain:w300
f:id:kgmyshin:20191202194128j:plain:w300 SRPなどを重視しつつ、バランス見てDRYしていきましょう

その後

上記のような座学をやった後に、実際にクラス図を書いてもらったり、原則違反をしちゃってる例を修正してもらう個人ワークを行います。 その後、(なるだけ)普段一緒に働いてるチームメンバーな参加者とグループになってもらい、個人ワークの解答をそれぞれ見せ合っては解釈をチームの共通認識に落とし込んでいくという流れになっております。

所感

まだコンテンツ作り中なので、こういう例題あるといいよねみたいなのあればシュッとtwitterとかで投げてもらえると嬉しいです。

余談

自分のTシャツも登場させました

f:id:kgmyshin:20191206113631p:plain f:id:kgmyshin:20191206113635p:plain

明日は 弊社新卒 slme くんの技術ポエムがあがるそうです。 内容は聞いてないですが、私、とても期待しています。

「どれだけ近づいたのかな?」

こんばんは。

この記事は SHIROBAKO Advent Calendar 2019 二日目の記事です。 2015年からこのアドベントカレンダーを初めて今回で5回目です。続いてて良いですね。

最近は来年の映画でのエマの生活水準が向上しているのかどうかが気がかりな毎日です。(公開された動画を見る限り、はっきりとはしないけどどうやら向上してそう?)

個人で何かを作ったりみたいなものを除いた、会社で働くものとしてのキャリアパスを考えると結局は会社があなたに「どこまで何を任せられるか?」になる気がします。責任と言ってもいいのかも。

で、クリエイティブに身を置くものの場合は、大抵は次の式が成り立つような肌感を持っています。

責任 = 技術的難易度 × スコープ

他にも信頼残高とかいろんなパラメータがあると思いますが、一旦これで。

そういう自分も次のようなステップを通ってきました。

途中Androidアプリ開発以外もやってたので端折ってますが、概ねこういう感じでスコープがどんどん大きくなってきていることがわかります。 最近は次のようなことにチャレンジしてます。

  • 自分抜きでサービス開発において自立した状態をつくることができる
  • 会社全体スコープで特定領域(成長・育成等)においての課題解決できる

自分がSHIROBAKOで大好きな二人はどうでしょうか。

宮森あおい

宮森あおいは作中次のようなステップを踏んでいます。

  • 1話の進行を任せることができる
  • 最終話の進行を任せることができる
  • 制作デスクを任せることができる
  • (新入社員の世話を任せることができる)

担当スコープが徐々に大きくなっており、キャリアパスを登っていってることがわかります。 すでにナベPにはエースと呼ばれていますしね。 映画でどういったキャリアパスを進めるのか楽しみですね。

安原絵麻

絵麻はどうでしょう。

  • 動画を任せることができる
  • 原画を任せることができる
  • 作画監督補佐を任せることができる

あおいの時もそうでしたが、自分に知識があまりないためステップ数が少ないのはご勘弁を.. 絵麻も着々と、着々とやっていってます。 映画でまさか作監になるってのは...どうでしょうか。

自分が見たい5人の未来

最初の前提のところにちらっと書いた「個人で何かを作ったりみたいなものを除いた、会社で働くものとしてのキャリアパスを考えると」ですが、個人で何かを作ったりを含めるとキャリアパスはぐいっと広がります。 夢もあるあしね。 個人的には、ある程度成長した5人が、仕事の合間に集まって自主制作「神仏混淆七福陣」を作る未来を見たいと思っています。お金があれなら、クラウドファウンディングとかどうですかね。今の時代っぽいし。 でも、やっぱり時間的に厳しいのだろうか。映画での進捗が私、気になります。

「私たち。夢に飛び込んで、夢を仕事にして。でも、七福神にどれだけ近づいたのかな?」

www.youtube.com

映画で進捗はあるんでしょうか。

近況

入社してそろそろ一年半が経とうとしています。 最近霊圧を感じないと言われることもありましたし、そもそも呑み自体を減らしてるのもあって最近会ってないなぁと思う人も増えてきたので、 知り合い向けにここで近況でも報告してみようと思った次第です。

お久しぶりです。

入社した経緯

前職をやめた時に書いた記事を読み直してみました。

じゃぁなんで辞めるのか

若手に引き継いで回った結果やることがなくなったというのが大きな理由です。

自分がやるよりは若手の成長機会や実績にした方がいいよなって思ってたら (いまでもそれは正しいと考えています ) 、

いつの間にか雑務やマネージメントばかりになって技術者としての自分を維持できるかどうか不安になってしまったんですね。

ここには書いてなかったですが、自分は会社全体のAndroidエンジニアのボトムアップとかをやってみたいなぁと思ってました。 その部分に関してはある程度の自信を持っていたのもありまして、そう考えたいたらちょうど今の弊社がそういう人を探していたということで、特によそ見することなく転職することとなりました。

入って1年くらい

Androidエンジニアを探す旅

入社してまずやったことは社内でAndroidエンジニアを探すところから始めました。 外部から急に「レビューしたげるからリポジトリの閲覧権限くだされ」と言っても「だれやねん」となりかねない(少なくとも自分が言われたらそうなる)ので、 知り合いをつてに知り合ったAndroidエンジニアの方々に「困ったら声かけてくださいね」と挨拶して回るみたいな地味なところから始めた記憶があります。 自分が過去に発表した資料だったり、ブログだったり、果ては技術書典でだした本だったりを持ってくれてる人もいて、それらきっかけに逆に話しかけてくれる人もいて、嬉しかった記憶があります。 Androidエンジニアで横断の懇親会とかも開きました。またやろうかな。

アウトプット

またこの時期は「弊社にもAndroidエンジニアいるんだぞ!!!」という意気込みで、より一層ブログ記事を書いたり、登壇したりに力も入れてました。

いろんな開発チームのレビューに参加したり

ある程度弊社のAndroidエンジニアたちと顔見知りになったころらへんから、複数チームのレビューに参加するようになりました。 中のコードをみつつ、レガシーなプロジェクトなどにあたりをつけつつ、どう生産性を上げていくか考えてました。 いろいろ考えた結果、参考になるようなリポジトリを作ることにしました。 Googleの出してる android-sunflower だったり、 Blueprints的なあれです。

これがちょうど完成した頃に Google IOがあり、そこで Jetpack が紹介され全ては水の泡となりました 😇

事業部にガリッとコミット

Google IOから帰ってきてから今年の年始くらいまでは一つの事業部にがっつり入ってました。 そこでは Jetpackガリガリ使ったモダンなプロジェクトとしました。

始めはAndroid側のリードエンジニアみたいな動き方も多少してましたが、自分がいないと回らない状況は目指すべきところではないので途中からは現場Androidエンジニアにバトンタッチして、自分はクリティカルな何かが発生した場合にたまに顔を出すマンとなりました。 最後の方は自分のやることはどんどん減っていって、リリースが近づくほど僕は暇になっていきました。

今となってはもうそこの開発に直接関わってはいませんが、 そこのエンジニアは優秀ばかりで、勉強会で登壇したり、別チームからのAndroidの質問などにも積極的に答えてくれるのですごく助かってます。 そこにいたすでに他チームに移った一人の若手が、先陣切ってコンフルでクラス図書いてしっかり設計したものを、周りの技術者と議論しながら進めているページを発見して、感慨深くなったりしてます。

近況(ここ半年くらい)

とあるチームのリーダーに

ここからがようやく近況です。

最近はとあるチームのリーダーをやらせてもらっています。 自分のチームは一言でいうと「社内改善をして露出を増やして、どんどんアトラクティブな会社にする」ことにコミットするチームです。 いろんなことをやってるので抜けがあるかもですが、カテゴリ的にはこんな感じです。

  • Android(モバイル)
    • モバイルエンジニアのボトムアップ
    • モバイルエンジニアコミュニティづくり
    • アウトプットしたり、促したり
  • 人事の方々との連携
    • 採用に対する課題解決
    • 評価に対する課題解決
  • 技術広報系
    • イベント企画、開催、運営
    • 予実管理
    • アウトプット促す仕組み作り
  • 1on1系
    • チームメンバーと
  • 事業支援
    • 現場のエンジニアで回るように

細かくは話しませんが、たくさんある大きな課題にガンガン体当たりしていってる毎日です。 今までは人事の方々との連携も個別にしかできてなかったのですが、弊チームができたことでより連携もやりやすくなって、イイ感じになってきています。 これだけ見るとエンジニアやってなさそうに見えますが、コードもガリガリ書いてます。それはもう。 特に最近もろもろのピークが重なり忙しくはあるんですが、どれも自分のやりたいことのど真ん中なのですごく楽しくやれてます。 どんどん改善回していくぞ〜〜〜〜

副業

副業先では変わらずAndroid系の技術顧問的なことをさせてもらってます。 大小関わらずに技術的に悩んでるところにアドバイスするのはそうなんですが、こちらでも組織的な課題でサポートできるところを見つけていきたいなと思う今日この頃です。

また、最近はScalaを書けるお話も頂けたのでScalaやっていきなお気持ちです。

個人開発

なるべく帰ってからは少しでも良いので個人開発をするようにしています。

これをガリガリとモノレポなKotin MPPでちょっとずつ進めていっています。 なんか面白い話溜まってきたら、どっかで話しますね。

私生活

実は最近、徐々に痩せてきました。やったぜ。

おわりに

  • なんか似たような事してる人(技術広報とか横断組織とかとか)いたらご飯いきましょ〜
  • 最近会ってない人ご飯いきましょ〜
  • 弊社に興味ある人ご飯いきましょ〜

DroidKaigi 2019に参加しました

こんにちは、 kgmyshinです。

DroidKaigi 2019で「マルチモジュールプロジェクトでのDagger2を用いたDependency Injection」というタイトルで発表してきました。下記が登壇動画となります。動画すごく早く上がってきましたね!編集ありがとうございました!

www.youtube.com

スライド作り大変やった

とても難産でした。

ほとんどがコードでの説明になるのでどうやって飽きさせず、わかりやすく説明できるだろうか。 ここが特に難しかったポイントです。 話す内容は箇条書きにして決まっている。ただ、そこからスライドに落とし込めない。こういう状況でした。

自分は説明する際によくホワイトボードを使います。 ということで、今まで通りにホワイトボードを使ったら、自分はどう言う説明をするのかを試してみました。 家にホワイトボードはなかったので、iPadApple Pencilを使って メモ帳 アプリを開いて、トピックごとに図を書いたりコードに書き込んだり好き勝手に一人説明デモをしてみました。結果、満足のいくものとなり、味をしめ、それを細切れにすることでスライド作っていくことにしました。

もし、自分の発表を見た方がいれば、ホワイトボードで説明されてるように感じた人がいるのではないでしょうか?(いないかも?)

iPadApple Pencilでメモ帳で落書きしているのを、Keynoteに落とし込む際に「Keynote」でも同じようにできないのかしら?と思って試したところ、普通にできました 😇

f:id:kgmyshin:20190220230212j:plain

iPadMacで共同編集モードでKeynoteを開き、手書きしたいときはiPadで、タイピングしたいときはMacでという風に進めていきました。 普段は矢印でポイントとポイントを結ぶだけでもしんどいのですが、Apple Pencilを使えばスッと線を引くことができます。

f:id:kgmyshin:20190220231814j:plain

また、コードのとある場所にフォーカスを当てて何かコメントしたいときも、そのまま手で書いちゃえます。

f:id:kgmyshin:20190220231655j:plain

テキストボックスや図形から矢印を選択して、配置して、大きさ調整して、、、とやっていくよりも断然にこちらの方が早く楽しくスライド作りすることができました。

ただ一点だけ問題があります。それは、iPadMacでの同期が10秒ほどかかる場合があることです。 これだけはとてもストレスフルでした。 それでも、メリットの方が大きいと感じています。

当日発表したスライドは下記にアップロードしていますので、興味ある方は見てみてください。

android-multi-module-with-dagger - Speaker Deck

ブース

登壇以外の時間はほとんどの時間を弊社ブースにいました。

こんなツイートをしたところ、何人か話しかけてくれたり、実際に相談に来てくれたりして、自分としてもいい時間を過ごせました。

ラクーア

数年続けてDroidKaigiの1日目が終わったら、その足でいつものメンバーでスパラクーアにいくやつをやってるのですが、今年もいってきました! yome.fmでも語られてるので聞いてみてください。

yomefm.github.io

1日目で全てを出し切って、爽快感がやばかったです。 2日目の朝に展示ルームでサンドイッチを食べて、その足で新宿駅の逆側の一蘭に行っちゃうくらいはハイになってました。

緊張

今年はあんまり緊張しませんでした。 昔は手が震えるくらいガチガチに緊張してたころもあった気がするのですが,,, ただ、本番の方がリハに比べて5分くらい早く終わるので、ゆっくり話せるよう気をつけたいところです。

リハが良かったのかも

自分を含めた登壇者n人すごい褒めてくれるm人 で、DroidKaigi本番の数日前にリハを行いました。 これがすごくよくて、自身に満ち溢れることができました! またやりたい。

わかりやすい説明ってなんだ

わかりやすい説明とは何か、きっと下記を満たせばいいのだろうと今までなんとなく思ってきました。

  • 課題が明確
  • ゴールが明確
  • 課題とゴールの間の説明に飛躍がない

ただ、しっかり検証したわけでなく、ずっとただの仮説のままでしかありません。 ちゃんと検証して「わかりやすい説明とは」の言語化に努めようと思った資料作り & 発表でした。

来年も!!

できれば来年も何かしら発表するぞ!!!

Vue + Vuex + TypeScriptでTODOアプリを作ろうとして、registerModuleとかで試行錯誤した話

この記事は 28日目の DMM.comアドベントカレンダー(?)の記事です。

1ヶ月前くらいから作ってみたいサービスができまして、一旦webだけで、雑なやつでいいやと思い Vue.js + TypeScript でこつこつと作り始めております。 私、普段はAndroidエンジニアをやっているため、Vue.js も TypeScriptも普段触れておらず、ドキュメントを読んだだけの初心者なため、正しいことを書けている自信はないので、その点はご注意くださいm m 「できてへんやんけ、基本的なことが〜〜〜!」と思う箇所が記事中にいくつも出てくるかも知れません。もしそう言うのが耐えれなそうであれば、ここでぜひ離脱を...。

この記事は、一言で言うと「Vuexを導入してみて、動的にモジュールをStoreに追加する方法を試行錯誤してみたときのメモ」です。 ベストプラクティスや、他の方がどうやってるかなどを調べてもうまく見つけられなかったので、ゴリゴリやって見たよ、こんな感じかしら?と言う記事です。 もしベストプラクティスや知見をお持ちの方いらっしゃれば、ご教授いただけると幸いです。

Vuexを使う理由

サービスづくりを開始してみて、ある程度の規模になりそうなことが見えてきたので、 秩序立てた設計が必要だなと思い始めたのが理由の一つです。

Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。

Vuexはライブラリでありながら、ある程度設計を矯正できるもので、またVue.jsを使う上でメジャーであるため採用しました。

作るTodoアプリのコンポーネント構成

そもそもなぜTodoを作りたかったのかと言うと、作りたいサービスで 動的にモジュールを追加・削除すること が必要になってきたからです。 Todoのサンプル自体は巷にたくさん出回ってるのですが、コンポーネントが1つだったり、モジュールを使ってなかったりで自分の条件を満たすものはありませんでした。

f:id:kgmyshin:20181227172730p:plain

上図の左のようなものではなく、右のように細かくコンポーネントを分けていきます。 このままの仕様であれば、左の構成でも問題ないのですが、例えば一つのTodoに対して、 編集できたり、コメントできたり、リマインダーをつけれるようになったり、さらにサブタスクをつけれるようになってくると耐えれなくなってしまいます。

設計あるいはルール

どのコンポーネントでも、何も考えなくてもスラスラかけるくらいには統一した書き方ができるようなルールを目指しました。 現状は下記で落ち着いてきました。

ComponentとStoreの対応関係

Component一つに、一つのStore(実際はStoreに登録されているModule)を用意する

f:id:kgmyshin:20181227172831p:plain

一つのStoreで複数のComponentを見るようになった場合、コンテキストの違うイベントやStateを持たなければならず、可読性が下がります。 一つのComponetに対して一つのStoreと細かくすることで、Storeを簡潔にすることができます。

f:id:kgmyshin:20181227172848p:plain

Storeの書き方

Storeは https://github.com/ktsn/vuex-type-helper を使って書く

趣味なところもあります。よりTypeScriptな方向に寄せたかったためです。READMEにある書き方をそのまま踏襲しました。

Componentの書き方

先に例を。最終的にこの書き方に落ち着きました。

@Component
export default class NewTodo extends Vue {
  public beforeCreate() {
    const { mapState, mapGetters, mapActions } = createNamespacedHelpers('todos/new');
    this.$options.computed = {
      ...mapState(['body']),
      ...mapGetters(['submittable']),
    };
    this.$options.methods = {
      ...mapActions(['submit', 'update']),
    };
  }
}

一言で言うと コンポーネントでは「どのStore(Module)を使って、どのState, Getters, Actionsを使うかを定義するだけにする」です。

beforeCreatecreateNamespacedHelpers を使って mapXXX を生成してオブジェクトスプレット演算子でcomputedやmethodsに突っ込むのみです。 下記がポイントです。

コンポーネントでローカル変数はなるべく使わない

「Vuex を使うということは、全ての状態を Vuex の中に置くべき、というわけではありません。」

と公式に書かれているのですが、「Vuexに置くべきか否か」の閾値が見えそうになかったので、一旦全てVuexに寄せることにしました。

namespaceに使うものを props で受け取る場合は例外。

関数も定義しない

一旦はどんなユーザーイベントも全てアクションを投げれば良いはず、表示に必要なものはStateやGettersにあるはずという方針でやっていきます。

mapXXXには原則、文字列配列を渡す

Storeは vuex-type-helper 形式で別ファイルに定義しているはずなので、ここで改めて定義することはないはず。

mapMutationsは使わない

MutationへのCommitはコンセプトの図通りActionからだけにしたい。

Storeは別の場所で実装されており、基本的にComponentのHTML部分からは、どのユーザーイベントがどのActionになるかを書くかだけなので、ComponentでMutationにコミットすることは必要ないはず。

beforeCreateでやる理由

あとで詳細について触れますが、propsから取得したものからcreateNamespacedHelpersを使ってnamespaceに含めたいことがあるから です。 (ただ、これは他にやりようあるかもしれないし、できるならば@Component Decorator で記述したい)

もちろん実績がないので、大きな規模になれば例外も出てくるルールだと思いますが、TODOアプリレベルなら全然おさまったし、今回はチーム開発でもないので良しとしました()。 例外もたくさん出てくるでしょうが、それは対面した時に考えていくとして、進めていきます。

悩んだところ

動的にモジュールを追加・削除する

コンポーネントと、それに紐づく Store(Module)の パス は下記のようになっています。

f:id:kgmyshin:20181227172920p:plain

やりたいことはタスクを追加した際に、パスが todos/{タスク5のid} のモジュールが新しくStoreに追加されて、 タスク5のコンポーネントが描画されて他のコンポーネント同様に正しい挙動をすることです。

新しいモジュールを追加する処理は Store#registerModule でできます。 悩んだのはこのメソッドを どこで呼ぶか でした。

一番初めは、下記のように TodoListStore の Mutation の中で呼ぼうかなとうっすら考えてました。

const mutations: DefineMutations<ITodoListMutations, ITodoListState> = {
  add(state, { todo }) {
    state.todos.push(todo);
    store.registerModule('todos/' + todo.id, createTodoModule(todo));
  },
  ...
};

ただ、このままでは storeインスタンスをどこから引っ張ってくるかという問題と、 そもそも、 Mutation は State に対する Mutation なので、 Storeの変更処理もここに書くのは違うかなと思いました。

最終的には Plugin で解決することにしました。 Pluginは Mutation への commitのフックをできることができるものです。

下記のような関数を作り、storeに登録することができます。

const plugin = (store: Vuex.Store<any>) => {
  // 初期化時に存在する todo から TodoStoreを生成して registerModuleする
  state.todos.forEach((todo) => {
    store.registerModule('todos/' + todo.id, createTodoModule(todo));
  });
  store.subscribe((mutation, _) => {
    if (mutation.type === 'todos/add') {
      // 追加時
      const todo = mutation.payload.todo as Todo;
      store.registerModule('todos/' + todo.id, createTodoModule(todo));
    } else if (mutation.type === 'todos/remove') {
      // 削除時
      const todo = mutation.payload.todo as Todo;
      store.unregisterModule('todos/' + todo.id);
    }
  });
};

これを、ルートの Storeにスッと登録することで動きはするのですが、 本来であれば TodoListStore のための Pluginであるはずなのに、それを大元の Storeに置くのはこれいかにと思い、 モジュールにも Pluginを持てるようにすることで解決しました。

正直このやり方はフレームワークを拡張している点が、あまりスマートではないと思っています。 ただし、個人開発だしな...ということで一旦これで良しとしました。

propsから渡したものを使って動的にネームスペースを指定したい

Todoコンポーネントで、todos/{タスクnのid}のパスのStoreを指定する方法を考えていきます。 まずは親の TodoListコンポーネントでidをTodoコンポーネントに渡します。

      <li v-for="todo in todos" :key="todo.id">
        <Todo
          v-bind:id="todo.id"
        />
      </li>

そしてTodoコンポーネントでは、次のように id を元に namespace を作り、各 mapXXX を作ります。

@Component
export default class Todo extends Vue {
  @Prop() private id!: string;
  public beforeCreate() {
    const { mapState, mapGetters, mapActions } = createNamespacedHelpers(`todos/${this.id}`);
    this.$options.computed = ...
  }
}

このように書きたかったのですが、このコードは動きません。 まず id には beforeCreate の段階ではまだ値が入っていません。 また逆に、created() では this.$optionscomputed などを差し込むことができません。 「あ、詰みかな...」と思ったのですが、同様のことをやりたい人はそこそこいるらしく、関連したissueがありました。

github.com

ありがたいことに、このissueにてktsnさんから解決策が提示されております。

{
  props: ['namespace'],

  computed: mapState({
    state (state) {
      return state[this.namespace]
    },
    someGetter (state, getters) {
      return getters[this.namespace + '/someGetter']
    }
  }),

  methods: {
    ...mapActions({
      someAction (dispatch, payload) {
        return dispatch(this.namespace + '/someAction', payload)
      }
    }),
    ...mapMutations({
      someMutation (commit, payload) {
        return commit(this.namespace + '/someMutation', payload)
      })
    })
  }
}

このように、関数実行時に namespace を取得するようにすれば(その時点では propsは取得できてるので)いけるよとのことのようです。 初めはこのやり方でやっていたのですが、書くこと多いな...と思い始めて、createNamespacedHelpersのコードを参考にしながら、 namespaceを返す関数 を引数に受け取るヘルパーを作成しました。 ヘルパーを通して見栄えをよくしてるだけで、実質的にはktsnさんのソリューションとそんなに変わらないです。 こんな感じです。

export default class Todo extends Vue {
  @Prop() private id!: string;
  public beforeCreate() {
    const { mapState, mapGetters, mapActions } = createNamespacedFnHelpers(() => `todos/${this.id}`);
    this.$options.computed = ...
  }
}

使うヘルパー関数が違うだけで、props から namespace を作るときは createNamespacedFnHelpers を、 固定値の場合は createNamespacedHelpers を使うという簡単なルールなので、迷うこともないでしょう。

見比べるように、固定値で namespace 指定バージョンのコンポーネントも置いておきます。

@Component
export default class NewTodo extends Vue {
  public beforeCreate() {
    const { mapState, mapGetters, mapActions } = createNamespacedHelpers('todos/new');
    this.$options.computed = ...
}

できた!

f:id:kgmyshin:20181227173007g:plain

リポジトリはこちら

ちゃんと学んで整理したあとに公開したかった気持ちもあるのですが、やっぱり見れないと意味なと思うので一応置いておきます

github.com