LayerX エンジニアブログ

LayerX の エンジニアブログです。

FlutterアプリにおけるUI Component Architecture #LayerXテックアドカレ

こんにちは。バクラク申請・経費精算 ネイティブアプリエンジニアのyoheiです。

最近はこたけ正義感の逆転裁判プレイ動画を見ながら法律の勉強してます。好きなラジオは真空ジェシカのラジオ父ちゃんです。M-1も応援してます!

この記事はLayerXテックアドカレ2023の26日目の記事です。前回は 赤羽さん が「Go言語のORMであるGORMをv1からv2へのマイグレーションした話」を書いてくれました。27日目の記事 id:itkq さんより「勤怠をいい感じにする社内Slackアプリ #LayerXテックアドカレ - LayerX エンジニアブログ」ポストされました。一緒にご覧いただけたらと!

バクラク申請・経費申請では現在のモバイルアプリをFlutterでのリプレースを進めています。そのうえでチームとしてUIコンポーネント(Widget)をどの用に作っていくか設計(UI Component Architecture)を決め開発を進めています。

ここ最近、Atomic DesignからLayerX独自の設計に落ち着いたので、なぜAtomic Designから独自設計に移行したのかを紹介していきます。

(※Atomic Designについては色々なサイトで紹介しているので、今回は割愛させていただきます)

アプリで使用している技術

まず、Flutterアプリで利用している技術の一部を説明していきます。

  • API
    • GraphQL
  • 状態管理
    • Flutter hooks(1UIコンポーネント内の状態管理)
    • Riverpod (UIコンポーネントをまたぐ場合の状態管理)
  • Widgetカタログ

Atomic Designを採用した理由?

LayerXでは元々WebアプリでAtomic Designを採用していたため、それに倣ってやっていこう。molecules, Organismsなど分割していけばユースケースにハマり開発がしやすくなるのではと考え、Atomic Designを採用しました。

Atomic Designの構成

LayerXでのUIコンポーネントはAtoms, Molecules, Organisms, Parts, Templates, Pagesと6つの要素で構成しています。(Partsに関しては独自の要素になっています)

各要素を簡単に説明してきます。

Atoms

  • ページを構成するこれ以上分けられない最小構成要素
  • ドメインに依存しない

Molecules

  • 意味を持つ要素
  • Atomsを組み合わせて作成される
  • ドメインに依存しない

Organisms

  • サービスとして意味のある単体で機能する要素
  • Atoms, Molecules, Organismsを組み合わせて作成される
  • ドメインに依存する
  • Fragment colocationを定義はできるがAPI Callはできるだけしない(parts, pagesで行う)
  • ロジックを持つ

Templates

  • ページ全体の骨組み
  • Atoms, Molecules, Organismsを組み合わせて作成される
  • ドメインに依存する
  • ロジックは持たない
  • 1つの Parts or Page に1つのtemplateが存在する

Parts

  • Page未満、Organisms以上のUIコンポーネント
  • ドメインに依存する
  • ロジックを持つ
  • データ取得を行う

Pages

  • UIの最終形態
  • route定義としてでてくる
  • ドメインに依存する
  • ロジックを持つ
  • データ取得を行う

まとめると以下ようになります。

要素 依存可能UIコンポーネント ドメイン依存 Data取得(API実行など) Fragment Colocation許可
Atoms x x x x
Molecules Atoms x x x
Organisms Atoms, Molecules, Organisms o o
Templates Atoms, Molecules, Organisms o x x
Parts Templates o o o
Pages Templates o o x

Partsが生まれた背景としては、タブが複数個ありそれぞれの中身で別々のAPIを実行しているが、PagesでAPIを実行してしまうとデータが変更された場合などに画面全体(全てのタブ)が再描画されてしまうことになります。これではパフォーマンスが良くないので、PagesではないがAPIを実行する要素がほしい!となり、Partsが誕生しました。(GraphQLを使っているので、OrganismsはFragment Colocationの定義のみでAPIの実行はPagesのみで行うという設計でした)

なぜAtomic Designをやめたのか?

結論を言うと、要素数が多く煩雑さが増してしまい認識齟齬や無駄な記述が多くなり開発速度が出なくなったからです。

  • ダイアログやBottomSheetはOrganismsなのか、Partsなのか?
  • OrganismsでAPI実行許可されていないので、簡単なAPI実行する処理でもPartsを作らないといけない(複雑なOrganismsもあれば簡単なPartsも存在してしまって違和感があった)
  • 役割の変更によりレイヤーの移動が発生する変更コストがある(ex. Organismsで作成していたが通信が必要になったのでPartsに変更する)
  • すでに実装済みのUIコンポーネントだったが、Organisms、Partsの認識が揃っておらず重複したUIコンポーネントを作成していた
  • ロジックはFlutter Hooksにより隠蔽できていおり、Pages, Templatesの分割のメリットが無く無駄な記述が増えている

新たな画面を開発する時に、レビュアーとレビュイーで要素のレイヤーが合わず相談&手戻りも発生している状態でした。

New UI Component Architecture


これらの課題感からチームで話し合い、シンプルでわかりやすい設計にすることにしました。

以下の基準により要素を分けることにしました。

  • ドメイン依存の有無
  • RouteのDestinationとなれるかどうか

この基準に沿って3要素(Parts, Compounds, Pages)に分割を行いました。

ドメイン依存有無 RouteのDestinationとなれるかどうか 旧要素
Parts x x Atoms, Molecules
Compounds o x Organisms, Parts
Pages o o Page, Templates

余談ですが、弊社がコンパウンドスタートアップを目指しているのでCompoundsとなりました!

それでは、各要素の説明をしていきます。

Parts

  • どこのアプリでも使えるようなUIコンポーネント(パーツ)
    • Atomic DesignにおけるAtoms, Molecules
  • ドメインには依存しない
  • 状態は持たない
  • Parts同士を組み合わせても良い
    • Button + Labelを持つPartsも作れる
  • Widgetbookを活用しPartsを把握・確認できる

Compounds

  • ドメインに依存するUIコンポーネント
    • AtomicDesignにおけるOrganisms
  • ドメインに依存する
  • 状態を持ってよい
  • 共通パーツとして使う側面もあるため、データ取得は積極的に行わずFragment Colocationを利用する

Pages

  • UIの最終形態
  • route定義としてでてくる
    • DialogやBottomSheetはrouteの定義として出てこないのでCompoundsとして定義する

要素数が半分になり、基準も明確になったかと思います。

Atomic Designと同じ用にまとめるとこちらになります。

要素 依存可能UIコンポーネント ドメイン依存 Data取得(API実行など) Fragment Colocation許可
Parts Parts x x x
Compounds Parts, Compounds o o o
Pages Parts, Compounds o o x

おわりに


新たな設計に変更してから日は浅いですがUIコンポーネントを作成するための基準が明確になったり、不要な要素がなくなったことにより悩むことが少なくなり開発速度改善に繋がっていると実感しております!

モバイル開発について気になることがあればお話しましょう!

jobs.layerx.co.jp


ハタラクをバクラクにするプロダクトを一緒に開発していきたい人を大募集中です!もしご興味ありましたら、ぜひカジュアル面談からお話させてください!LayerXオフィスでカジュアルにドリンクを飲む会を企画しました。こちらも是非ご覧ください!

jobs.layerx.co.jp

採用情報はこちら↓ jobs.layerx.co.jp