こんにちは。バクラク申請・経費精算 ネイティブアプリエンジニアのchocoyamaです。
最近妻が身近な金継ぎ師を探しているので、もし身近な金継ぎ師がいらっしゃる方はご紹介いただけると嬉しいです。
この記事はLayerXテックアドカレ2024の23日目の記事です。
今回の記事では私たちモバイルアプリチームが実装アーキテクチャをどのように決定・運用しているのかをご紹介したいと思います。
前提情報
現在バクラクのモバイルアプリチームは、社員3名 + インターン生1名といったエンジニア構成でFlutter製のネイティブアプリ開発を行っています。
このアプリはフォームを中心としたプロダクトで、今年9月にストアリリースを行いました。
アプリの規模感としては、実装ボリュームがとても大きく、クライアント側でカバーすべき機能の膨大さから、私個人の経験としても過去最大レベルに複雑度の高いプロダクトとなっています。
そのため、実装アーキテクチャもこれまでに経験してきた定型的なもの(MVVM, Clean Architecture, etc)をそのまま適用するだけでは運用しやすい状態にはならず、随時チームメンバーと議論しながらプロダクトに合う最適なアーキテクチャを模索して改善し続けています。
現状の最新アーキテクチャ構成(※ 各役割の詳細は本記事では割愛) - Presentation Layer - UI:ユーザーに対するIFを提供するコンポーネント - Hook:ローカルな状態管理を行うためのコンポーネント - Behavior:UIレイヤーのロジックを実装するコンポーネント - Domain Layer - Provider:UIに紐づかないグローバルな状態管理を行うためのコンポーネント - Service:実際のデータ操作や外部APIとの通信など、具体的な実行手段を提供するコンポーネント - Specification:純粋関数として定義できるビジネスロジックを実装するコンポーネント - Model:Presentation Layerと連携するデータ定義と関連処理の凝集化を行うコンポーネント - Data Layer
前提となる設計の基本方針については、先日登壇させていただいたFlutterKaigi 2024で細かくお話しさせていただきました。もし興味を持っていただければ併せてお読み頂けると嬉しいです。
難しいポイント
バクラク申請・経費精算というプロダクトは、利用者側には極力シンプルなIFで利用して頂きたいという思いから、無闇にUI要素を増やして解決するといった手段は取っていません。その分それを意識させないための自動化対応が内部で無数に組み込まれています。
また、サポートする申請の種別も大量にあり、「汎用申請」「経費精算申請」「支払申請」など、一見同じようなフォームの中でも、用途に応じて異なるドメインが複数存在しています。
現在までの変遷の歴史
そういった前提のもと、各リリースフェーズで機能拡充を進めながら、その都度最適なアーキテクチャにするべく議論とリファクタリングを進めてきました。以下に各フェーズごとの改善サマリを記載しています。
このようにバクラクの機能追加は「単に1機能追加する」といったレベル感にとどまらず、アーキテクチャレベルでどのように実装を組み込んでいくかを都度検討していく必要がありました。
当然、初期段階からある程度将来性を見越した設計にはしているのですが、設計時に将来の機能実装を完全に把握し切ることが難しいという点や、オーバーエンジニアリングにならないバランスの考慮などから、各実装段階で都度最適な状態にリファクタリングをしていくといった進め方をしています。
設計の進め方
現在の設計の進め方には大きく以下の2パターンが存在します。
タイミング | オーナー | 目的 | 進め方 | |
---|---|---|---|---|
1. アドホックな設計と議論 | 大規模な機能実装時 | 各機能実装をメインで担当するエンジニア | 特定の機能実装に伴う課題に対応するため | 設計の叩きを用意し、チーム内に持ち込んで議論やモブプロを実施して詰める |
2. 定期的な議論とアップデート | 隔週定期 | チームメンバー全員 | 最新の状態を俯瞰し、標準化や認識合わせ、エッジケースの対応方針擦り合わせを行うため | 隔週のMTGで議論を行い、結果をその都度Design Docへ更新する |
ここでの話し合いの結果はDesignDocという形でドキュメント化しており、チーム共有ガイドラインとしてメンテナンスを続けています。(今回内容の詳細については割愛します。)
アーキテクチャの運用
また、議論して設計したアーキテクチャは適用していかなければ意味がありません。 具体的には以下のような対応を行なっていますが、それぞれ課題があります。
- リファクタリングのタスク化 → 機能実装とのリソースバランスが難しい
- テンプレートコード化 → まだドラスティックに更新が入るので効果が最大化されていない
- コードレビューでの担保 → 実装の最終段階での手戻りになるため修正コストが大きい
そのため、コードの最適化を極力開発フローの手前側で担保することが、全体での実装コストを削減するためには重要となってきます。
Cursorの活用
そこで、上記の問題に対するひとつの解決策としてCursorの利用を積極的に行なっています。
Cursor
Visual Studio Code(VSCode)をベースに開発されたAI搭載のコードエディタ。
コードの自動生成や補完、プロジェクト全体をコンテキストとして持たせたChatなどの機能が搭載されている
https://www.cursor.com/
CursorにはAIによる高度なコード補完機能の他に、AIチャットでのコンテキスト情報としてライブラリの公式ドキュメントや外部リソースを参照させる機能があります。 この機能は自身のチームでメンテナンスしているローカルのドキュメント情報も与えることができるため、チーム個別で定めた設計方針を学習させることも可能となります。
私たちのチームでは、繰り返しチーム内で議論してきた結果がDesign Docとして存在しているので、これをAIにコンテキスト情報として与えることで、「設計の壁打ちができるAIアシスタント」をCursor上に常駐させることが可能になりました。
具体的には、ルートディレクトリに .cursor/settings.json
という名前で設定ファイルを配置し、以下のようにコンテキスト情報を与えたいファイルのパスを指定します。
{ "customInstructions": { "includeFiles": [ "docs/*.md", "README.md" ], "projectContext": { "documentationPaths": [ "docs/", "README.md" ] } } }
その上で作成しているDesignDocを対象ディレクトリに置いておくだけで、その内容がAIにインプットされることになります。
AIとの壁打ち
以下に特定の実装方法に迷ったときのChat内容をそのまま掲載しました。
Cursorは前述したドキュメントの参照だけでなく、プロジェクト全体のコードを参照した上でのやりとりも可能なので、非常に高い解像度で返答をしてくれているのがわかるかと思います。 (内容についてはかなり細かいドメインの話になるので、雰囲気だけ感じ取ってもらえればと思います。)
また、この機能は実装時の利用だけでなく、定期的な議論の場面でも活用ができます。 AIをチームメンバーの一員として参加してもらうことで、より多角的に内容を詰めていくことも可能になります。
AIによるコードレビュー
ChatにはRuleを事前に設定しておくこともできるので、ここに参照して欲しい情報をセットしておくことで、ローカルでの事前コードレビューなども実行することが可能です。
(今回の設計の話とは若干逸れますが、これによりバグを発見できたこともありました。)
まとめ
以上のようにして、チームで策定しているアーキテクチャドキュメントの知識を与えておくと、一般的なコーディングプラクティスから一歩踏み出した、チーム固有の取り決めも考慮した上での相談が可能となります。
これにより実装コードに対するフィードバックタイミングをシフトレフトし、レビュワーのコストや修正コストを軽減することに活用ができます。
実際、私個人としては、ちょっとした悩みが発生した際の意思決定速度やコードレビュー完了までのスピードが早くなったと感じました。
またこの使い方はCursorの機能のごく一部であり、コード生成を活用したテスト実装の爆速化など、シンプルな実装スピードの向上などにも効果は抜群です。
これらの作業を1つのエディタ上で一貫して進められるのはとても便利ですね。
今後の展望
まだできてないことの1つとして、アーキテクチャ設計で固まった方針を実装テンプレートに落とし切れていないことが挙げられます。
この対応を進めていくことで、プロジェクト全体で実装コードのパターンが統一化され、運用のしやすさが向上するほか、AIによる改善提案や自動コード生成の精度向上にも期待ができます。
Figmaで定義されている情報と連携することでUI実装コストを大幅に削減することも可能かもしれませんね。