ニコニコ生放送でBCD Designを4年運用した知見(ベストプラクティス編)
この記事は ドワンゴ Advent Calendar 2024 24日目の記事です。
はじめに
株式会社ドワンゴのニコニコ生放送でフロントエンドを担当している misuken です。
今回は前々回の導入編、前回の基礎テクニック編に引き続き、BCD Designを4年運用した知見に基づき、BCD Designのベストプラクティスを共有し、実際に運用していく中で遭遇する、微妙な分岐点に対して、どのように対処したらうまく解決できたのかを、具体的な事例を交えて紹介していきます。
これは、システムを構成する様々な単語をどのように扱うと、意味や関係性が体系的になり、実装にピタリと連動するのかを論理的に追求するものであるため、BCD Designを使っていなかったとしても、参考になるヒントが見つかるはずです。
概要
- ディレクトリ構成術
- 最初に役立つ知識
- 迷ったときに役立つ知識
- より先進的な使い方
ディレクトリ構成術
ここではディレクトリ構成に関するベストプラクティスを紹介します。
分類ディレクトリ名の変更
BCD Designのディレクトリ名は Base Case Common Domain が標準ですが、DomainではなくFeatureを使いたい場合はディレクトリ名を変えても問題ありません。
依存関係の面から順に Base Case Common Domain の関係性にはなっていたほうがわかりやすいと思いますが、それぞれの名前をカスタマイズすることは可能です。
BCDにした理由は、わかりやすさと覚えやすさを重視しただけなので、プロジェクトによって分類ディレクトリの名前が変わっても特に問題はありません。
package by feature
どの局面においても、必要な情報が必要な場所にまとまるアプローチがあります。以下記事では、なぜうまくまとまるかの原理から丁寧に解説しているので必読です。
アンチパターンを理解して package by feature へ を参照。
派生系のまとめ方
元のコンポーネントから派生したコンポーネントが必要な場合のまとめ方。
関心の対象自体ごとにまとめる
コンポーネントを関心名のディレクトリでまとめる場合、先頭の単語が一致するからといって全部まとめるのは避けるべきです。
関心の対象自体を扱うものだけをまとめるようにしましょう。
domain
program
program-card ✅ 番組自体を対象にしたものである
program-comment-card ❌ 番組自体を対象にしたものではない
番組コメントは番組に紐づいているものであって、番組自体の関心ではありません。programの中に入っていいのは、番組自体を表現するものか、番組の構成物であるサムネイルや番組タイトルを表現するものです。他にも番組放送者なども番組に紐づいているものなのでprogramの中に含めるべきではありません。
domain
program
program-card ✅ 番組自体を対象にしたものである
program-comment
program-comment-card ✅ 番組コメント自体を対象にしたものである
修飾語を含む関心のまとめ方
関心名をpreやspecial等で修飾しても主体が変わらない場合、新しい関心とするのは避けるべきです。pre-xxx や special-xxx でまとまってもメリットも無く、関心の千切りになるだけです。
domain
member
member-registration-form
pre-foo
pre-bar
pre-member ❌ 会員種別が散らばってしまう
pre-member-registration-form
special-foo
special-bar
special-member ❌ 会員種別が散らばってしまう
special-member-registration-form
プレ会員やスペシャル会員は、会員の一種であり、会員であることに変わりはありません。
会員のバリエーションに該当するため、共通部分をスマートに管理するディレクトリ構成と同じ方法が有効です。
domain
member
_base
member-registration-form
pre-member ✅ 会員種別がまとまる
pre-member-registration-form
special-member ✅ 会員種別がまとまる
special-member-registration-form
これとは別に、program-comment と blog-comment のように、commentのバリエーションではなく、番組におけるコメントやブログにおけるコメントのように、関心Aで利用するBが成立する場合は、新しい関心とするべきです。
common
comment ✅ 汎用的なコメント
domain
blog-comment ✅ ブログにおけるコメント
program-comment ✅ 番組におけるコメント
最初に役立つ知識
ここではBCD Designの基本的な使い方に関するベストプラクティスを紹介します。
コンポーネント名に複数形を使わない
コンポーネント名に Images のような複数形を使用すると、実装レベルで微妙に噛み合わないところがでてきますが、ImageListを使う形にすると常に安定します。
リスト系のコンポーネント名 Images と ImageList はどっちが有利かを参照。
Siteドメインの有効性
ページのヘッダやフッタはコンポーネント名が Header や Footer となりがちですが、BCD Designではそれらの名前は Base に該当します。Header や Footer だけでなく、SideBar なども様々なドメインを参照して表示する側であるため、Base に置くと依存関係の上で問題になります。
こういったコンポーネントは内容がサイトという概念に依存して構成されたものであるため、Siteドメインを作成し、その中にまとめる方法が有効です。WEBの世界にサイトマップという言葉があるように、サイトだからこそ存在(サイトの概念に依存)しているUIがあるのです。
domain
site ✅️ サイトの概念によって生まれたものが全てまとまる
site-header
site-footer
site-map-section
site-navigation-menu
site-side-bar
また、ニコニコ生放送では、共通ヘッダと呼ばれるニコニコサービス共通のヘッダと、ニコ生固有のヘッダの二段構えになっていますが、これらは以下のように整理されています。
domain
nico
nico-common-header ✅️ nicoドメインで共通のヘッダ (実質社内ライブラリを読み込む処理)
site
site-header ✅️ サイトドメインのヘッダ
常に、 site-header はそのサイト独自のものになりますが、系列サイトを作ったとしても、同じコンポーネント名で運用できて便利です。
Domain名で手を抜かない
TagやCommentといった、Commonに該当する抽象的な関心名は、原則としてはDomain名を頭に付けることをおすすめします。
ニコニコ生放送では当初、番組のタグに対してTagを使用していたところ、あとから好みタグ(現名称は "好きなもの")が現れた影響で、すべてのTagをProgramTagに変更しました。さらに、番組のコメントでも、依存関係の問題で、すべてのCommentをProgramCommentに変更し、大変面倒な思いをしました。
依存関係の問題とは、ProgramTagのようにDomain所属の関心であれば、同じくDomain所属の関心への参照が可能ですが、TagのようにCommon所属の関心は、Domain所属の関心を参照できないという問題です。
何かの機能追加のタイミングで、依存関係が破綻するリスクを抱え続けることになるので、関係性があるのであれば、最初からDomain名を付けておくことが得策です。
これがYAGNI原則にあたるか?
これがYAGNI原則にあたるかに関しては、最初の実装時点で関係が明らかならば、あたらないと考えます。頭にDomain名を付けない理由が名前の短さならば、原則は付ける方針として、よほど将来に渡って影響が無いことが確実なもののみ省略を検討、というスタンスのほうがお得です。
なぜなら、ドメイン名に一単語増えるだけのコストが、あとからすべてリネームするコストを上回るとは考えにくいからです。そもそもリネームする状況に至る前には、何かしらの運用上の問題が生じ、作業コストや検討コストが発生しているはずで、思った以上にコストが掛かっています。
少しのコンパクトさより、積み上げやすさのほうが大切です。
TagやCategoryをUI名にしない
TagやCategoryはドメインとしても、UIとしても使いたくなるややこしい存在ですが、UI名には使わないほうが曖昧さの回避に繋がります。その理由は、TagやCategoryは抽象度の高い関心であり、他のコンポーネントと同様に、関心をどのUIで表現するか?という法則性に従うためです。
最近は Chip というコンポーネントが一般化されてきたようなので、TagChip として扱うと BCD Design でも上手に扱えます。
構成 | 例 | コンポーネント名 |
---|---|---|
(2)何を (3)どうする (4)UI |
タグ(を表示する)チップ | TagChip タグチップ |
ニコニコ生放送では、TagCardというコンポーネントで扱っていましたが、将来的にはTagChip に統一していく予定です。
Bannerの役割を定めておく
BannerというUIは、広く一般的に使用される画像等の旗やのぼりを表現するUIと、アプリで通知などに使用するモードレスなUIの2つが存在します。
これらは名前が競合するため、プロジェクトでは予めBannerが何を指すのか、決めておく必要があります。特に、両方使用する場合、同じ名前で使用することは困難であるため、名前の使い分けが必要です。
そこで、比較的どのプロジェクトでも競合しにくく、BCD Designでも使いやすい名前を考えてみたので参考にしてみてください。
コンポーネント名 | 役割 | 備考 |
---|---|---|
VisualBanner | 旗 | 画像だけでなく、動画やテキストの場合もある |
NotificationBanner | 通知 | デジタル庁のデザインシステムに同等のUIが存在 |
通常は以下の3つの方針から選ぶことになるかと思います。
方針 | 旗 | 通知 |
---|---|---|
旗優先 | Banner | NotificationBanner |
通知優先 | VisualBanner | Banner |
中立 | VisualBanner | NotificationBanner |
迷ったときに役立つ知識
ここではBCD Designで迷いがちな部分の解決策について紹介します。
動詞から始まるUIの所属先
基礎的なコンポーネントでありながら、動詞から始まるUIが存在し、BaseとCaseどちらに所属するか迷うことがありますが、動詞がUIと関心のどちらに作用するかで判別できます。
例 | 動詞の作用の対象 | 所属 |
---|---|---|
ToggleButton Pulldown系 Dropdown系 |
UI | Base |
SearchButton | 関心 | Case |
ToggleButtonのToggleは微妙な存在ではあるものの、一般的にUI自体がトグル表現を持つため、Baseに所属します。
SeachButtonのSearchは何かの関心事を検索するものであり、UI自体に作用するものではないのでCaseに所属します。
ブレやすい単語リスト
末尾が以下のような単語で終わるコンポーネント名は改善できる可能性があります。こうすることで、コンポーネント名が形式に合い、説明的で、分類や並び順もキレイになり、美しく保守しやすい構成につながります。
改善前 | 改善後 | |
---|---|---|
Ad | AdBanner AdPanel |
広告は抽象度の高い関心名。 ❌ バナーを広告というUIで表現する ✅ 広告をバナーというUIで表現する |
Badge | BadgeIcon XxxIndicator StatusLabel |
Badgeの意味によって変わってくるため、詳細はBadgeからIndicatorへのパラダイムシフトを参照 |
Category | CategoryLabel CategoryChip |
カテゴリは抽象度の高い関心名。 表現するUI名が必要。 |
Footer | SiteFooter | 何のFooterなのかが不足しやすい。 |
Header | SiteHeader | 何のHeaderなのかが不足しやすい。 |
History | HistoryCard HistoryArticle等 |
履歴は抽象度の高い関心名。 表現するUI名が必要。 |
Illustration | IllustrationImage IllustImage |
Illustrationは図で抽象度の高い関心名。 表現するUIは画像やiframe等があり得る。 |
Information | InformationSection InfoSection InfoMessage |
情報は抽象度の高い関心名。 表現するUI名が必要。 |
Label | Label LabelChip |
単なる名称ならLabel。(LebelTextのText省略形) タグ的な分類用途ならLabelChip。 |
Menu | Menu MenuPanel MenuBar等 |
本当にMenu自体ならMenu。 Menu自体の周りに何か要素がある場合は、PanelやBarの中にMenuが入っている。 |
Navigation | NavigationMenu NavigationBar等 |
Navigationの意味は"道案内"なのでCase相当。 NavigationMenuはそれ自体がMenuの場合のみ。 |
NavigationBar | SiteNavigationBar | 何の道案内をするのかが不足しやすい。 |
Preview | PreviewImage PreviewDisplay等 |
Previewの意味は"試写"なのでCase相当。 必ずしも画像とは限らない点にも注意。 |
SideBar | SiteSideBar | 何のSideBarなのかが不足しやすい。 |
SideMenu | SideMenu SideBar |
それがMenu自体ならSideMenu。SideMenu自体の周りに何か要素がある場合は、SideBarの中にSideMenuが入っている。 |
Tag | TagLabel TagChip |
タグは抽象度の高い関心名。 表現するUI名が必要。 |
Thumbnail | ThumbnailImage ThumbImage |
ThumbnailView等も存在するように、Thumbnail自体は"縮小した見本"という修飾語として扱ったほうが安全。 |
Statistics | StatisticsList StatisticsBar StatisticsSection等 |
統計は抽象度の高い関心名。 表現するUI名が必要 |
Summary | SummarySection等 | 概要は抽象度の高い関心名。 表現するUI名が必要。 |
連想しにくいUI名リスト
内容が単純、または明瞭なUIは、それが何であるか連想しやすいものの、中には様々な内容を含んでいるUIや、特殊なUIは、それが何であるか連想しにくいものがあります。
そこで、連想しにくいUIの名前を、一覧にしてみました。対象のUIが該当するかしないかの判断に使っていただけると幸いです。
UI名 | 用途 |
---|---|
Controller | 操作するためのUIをまとめたUI |
Display | 何かを展示・陳列するスペースを表すUI。映像を表示する装置とは別の役割。 |
Dock | Launcherとほぼ同等の機能を有するUI |
Footer | 本体の情報に対する付加的な情報をまとめるUI |
Fieldset | 入力要素をグループ化するUI |
Header | 本体の情報に対する表題や概要をまとめるUI |
Launcher | 起動を補助する機能を有するUI |
Pane | 画面の一部を区切って表示する領域UI |
Panel | 画面上の部屋のようなもので、他から区別された領域を形成するUI。 区別された領域は、浮かせたり、はめ込んだりできる。 |
Player | 媒体の再生装置を表すUI |
Reader | 情報を読むためのUI |
Renderer | 描画する装置を表すUI |
Tool | ある目的を達成するために必要な機能や情報を集約したUI |
Toolbar | Roving TabIndexの機能を有し、道具が並べられたUI |
Viewer | 表示、閲覧するための装置を表すUI |
MenuとPanelの使い分け
普段何気なく使っているMenuとPanelも、MenuはMenu、PanelはPanelのように曖昧さを排除できます。
MenuとPanelの見極め方 を参照。
条件付きのコンポーネント名
実装方針によっては、通常のコンポーネント名に少し情報を加えたコンポーネント名が必要になることがあります。その場合はコンポーネント名の後方に With
For
By
といった前置詞を追加して追記します。
用途 | コンポーネント名 | 意味 |
---|---|---|
スタイルが適用されている場合 | XxxWithStyled | スタイル付きのXxx |
スマホ向けの場合 | XxxForSp | Sp向けのXxx |
ティッカーで表現する場合 | XxxByTicker | ティッカーによるXxx |
CaseとCommonで迷ったとき
例えば、何かの情報を取得した結果を表示するようなコンポーネントがあったとしましょう。
コンポーネント名を素直に表すとFetchResultInfoMessageやFetchResultInfoSectionのようになりますが、CaseとCommonどちらに入るでしょうか?
答えとしてはCommonになります。なぜなら、FetchResultは "取得結果" という、このコンポーネントで扱われる対象を表しており、「Result を Fetch する」ものではないためです。
Caseは動詞から始まるコンポーネントを含める場所ですが、それは良いコンポーネント名の法則 (3)どうする (4)UI
の3の部分の動詞であるという点に気を付けましょう。FetchResultのFetchは名詞として使われているため、Caseには入りません。
より先進的な使い方
ここではBCD Designのより先進的な使い方について紹介します。
より攻めた使い方で依存関係も完璧にスッキリさせたい方におすすめです。
Caseの扱い方
長くBCD Designを使用して洗練させていくと、意外とCase内のコンポーネント数は増えないことがわかります。特にIconの種類をsmart-svgのようなCSSで表現するものや、propsで切り替えられる場合、一部を除いて汎用的な「(動詞)ボタン」はほとんど用意する必要がありません。
また、base/dialog
から case/close-button
を使用したいといった、不適切な参照が発生がしやすい問題もあることから、Caseのディレクトリ自体を無くす構成が検討されてきました。(Caseの概念は残ります)
記事公開時点でのニコニコ生放送でのCaseのコンポーネント名は概ね以下の通りです。
case
alert-dialog
clear-button
close-button
confirm-dialog
preset-form
prompt-dialog
search-box
search-button
search-form
select-all-checkbox-control
上の構成を、派生系のまとめ方を使った以下の構成に変更することで、Caseが無くなります。
base
button
_base
_variant
primary-button
secondary-button
clear-button
close-button
search-button
checkbox-control
_base
select-all-checkbox-control
dialog
_base
alert-dialog
confirm-dialog
prompt-dialog
common
preset-setting // 扱っているものは プリセット設定 である
preset-setting-edit-form
search-word // 扱っているものは 検索ワード である
search-word-combo-box
search-word-submit-form
これにはポイントを二つ押さえる必要があります。
- 汎用的な派生系の一種としてBaseに置くもの
- 動作だった単語を、扱っているものに捉え直して、Commonに置くもの
2は、検索がワードだけでなく、その他の条件も持つ場合はSearchConditionSubmitFormのようにできます。これにより「検索条件を提出することで、検索結果を取得するフォーム」という捉え方になり、他のCommonにあるコンポーネント名と形式が揃います。
ちなみに、ニコニコ生放送ではすでにFollowToggleFormといった形でも採用されており、Followのような関係性を表すもの(他にも購読や契約も関係性)は、フォロー関係Follow(Relationship)
のように、暗黙の単語が存在すると捉えて、関係の着脱(フォロー関係の追加/解除)という概念に収束できます。
Errorの扱い方
Errorは名詞であるため、BCD DesignではCommonに分類される単語です。もしも、ErrorListのようなコンポーネントを作るのであれば、Commonが最適でしょう。自身でも以前の記事ではそのようにも書いてきました。
しかし、Error系をCommonに置くと、FieldやFormが標準でErrorListのようなものを持ちたいとき、CommonからBaseを参照してしまう問題が潜んでおり、完全にスッキリはしていませんでした。
そんな中、ErrorはStatus(状態)の一種であるという観点に着目したところ、エラーメッセージの捉え方の記事にあるように、ErrorはMessageなどの属性として扱ったほうが望ましいことがわかりました。
状態を属性にすると、errorだけでなく、warningやinfoといったものも、propsで切り替えられるため、わざわざそれぞれのコンポーネントを量産しなくて済むうえ、抽象度が一段階上がったMessageは、Baseに置くコンポーネントになるので、依存関係の問題も解消します。
つまり、ErrorListはMessageListという抽象度の高いコンポーネントで扱えば、丸く収まるということになります。
base
message
message-list
ちなみに、このMessageという単語を英辞郎で調べると、 "(伝えたい)意図" という使い方が最も合致するため、これは意図を表現するコンポーネントであると言えます。
改めて「Messageは、ユーザーにエラーの意図を伝えるコンポーネント」と文章にしてみると、かなり正確に明名できていることがわかります。
BadgeはUIとして使わない
Badgeは、ドメイン名とUI名で競合したり、そもそもUIとして解釈が複数存在するなど、ややこしい存在です。この問題はIndicatorという視点を持つことで、解消できます。
BadgeからIndicatorへのパラダイムシフトを参照。
まとめ
いかがだったでしょうか?
BCD Designを4年運用する中で得られた、ベストプラクティスの数々を紹介しました。
今回紹介した内容を活用すれば、誰でも迷うこと無くBCD Designの多大な恩恵を得られます。
最近は、先進的な使い方まで明らかになってきたことで、運用上の未解決課題はほぼ無いに等しいレベルまで洗練されてきました。すべての単語が過不足なく体系的に並ぶ様子には、美しささえ感じます。
また、仕組み的に流行り廃りの影響を受けないため、コンポーネント以外の分野でも応用が効きます。一度身に付ければ、様々な分野で一生役立つ、強力なスキルに繋がることでしょう。
まだまだBCD Designは一部の人だけが知る存在ですが、それではもったいないほどの魅力的な技術ですので、一人でも多くの方に知っていただき、恩恵に預かれることを願っております。
関連記事
2024-12-11公開: ニコニコ生放送でBCD Designを4年運用した知見(導入編)
2024-12-17公開: ニコニコ生放送でBCD Designを4年運用した知見(基礎テクニック編)
株式会社ドワンゴでは、様々なサービス、コンテンツを一緒につくるメンバーを募集しています。 ドワンゴに興味がある。または応募しようか迷っている方がいれば、気軽に応募してみてください。
Discussion