mcpField
N
naming-conventions

naming-conventions

github.com/zakariaf開発65161 回インストール6 週間前に更新
リポジトリ

naming-conventions について

Dart と Flutter 開発向けの命名規約スキルです。Effective Dart に沿って識別子、ファイル、アーキテクチャ上の役割を示す接尾辞を定め、コードの一貫性と検索性を保ちます。

主な機能

  1. 01Dart の型とメンバーの命名規則
  2. 02ファイルとディレクトリの命名規則
  3. 03アーキテクチャ層ごとの接尾辞
  4. 04インポートのグループ化と順序
  5. 05命名アンチパターンの確認項目

活用シーン

  1. 01新しい Dart 機能の名前を統一
  2. 02型名とアーキテクチャ上の役割を確認
  3. 03ファイル名とインポートを整理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/zakariaf/flutter-skills --skill naming-conventions

インストール後、Claude Code で/naming-conventionsを入力して読み込まれたか確認します。

詳細

命名規則

一貫性があり、役割のわかる名前を付けると、コードを検索しやすく、説明的にもなります。また、型の接尾辞によって、その型がどのレイヤーに属するかを示せます。これにより、レビュー担当者も、grep も、禁止インポートを検出するゲートも、名前だけでレイヤーを判別できます。このスキルが示すのは「方法」であり、規範となる「内容」は Effective Dart を参照してください。言語自体の規則に反する独自のスタイルを作ってはいけません。

絶対に守るルール

  1. 型名には UpperCamelCase を使います。 クラス、列挙型、ミックスイン、拡張、typedef、型パラメーターには、TaskScreen、OrderStatus、Predicate<T> のように付けます。一貫した形式にすることで、型を値と視覚的に区別できます。
  2. メンバー、変数、関数、パラメーターには lowerCamelCase を使います。 dueDate、loadTasks()、itemCount のように付けます。これは言語のデフォルトであり、これから外れると、読み手は一瞬立ち止まることになります。
  3. 定数には lowerCamelCase を使い、SCREAMING_CAPS は使いません。 const maxItemsPerPage = 50; のようにします。const MAX_ITEMS = 50 ではありません。Dart は C の慣習を採用しておらず、アナライザーは constant_identifier_names を想定しています。
  4. ファイル、フォルダー、ライブラリ、インポート接頭辞には lowercase_with_underscores を使います。 task_detail_screen.dart、features/task_detail/、import 'package:app_core/app_core.dart'; のようにします。クロスプラットフォームのファイルシステムと pub でこの形式が求められます。
  5. ファイル名は、そのファイルの主要な宣言をスネークケースにしたものにし、1 ファイルにつき主要な公開型は 1 つにします。 TaskNotifier は task_notifier.dart に置きます。utils.dart/helpers.dart/models.dart のような寄せ集めファイルや、utils//common//helpers//misc/ のような雑多なフォルダーは作りません。シンボルを検索する読み手が、そのシンボルを所有するファイルにたどり着けるようにします。core/ は、値オブジェクト、Result/Failure、Clock の境界、純粋な計算関数を置くために認められた純粋な基盤レイヤーであり、雑多な置き場ではありません。レイアウトを定義する project-structure-and-packages を参照してください。
  6. 3 文字以上の頭字語は単語と同じ形式で大文字・小文字を使い分けます。 Json、Http、Url、Api のようにし、JsonOrder、HttpClient、fromJson、imageUrl とします。JSONOrder、HTTPClient ではありません。英語で大文字表記される 2 文字の頭字語は、型名では ID、UI のように大文字のままでもかまいません。大文字・小文字が混在する頭字語は検索しづらく、一貫性もありません。
  7. 先頭のアンダースコアはライブラリ内限定を意味します。非公開にする意図がある場合にのみ使います。 公開シンボルの前に _ を付けて「名前空間」を作ってはいけません。別のファイルから使えなくなります。公開(_ なし)であることは、文書化された契約です。dartdoc-conventions を参照してください。
  8. ハンガリアン記法や型を示す名前付けは使いません。 strName、iCount、lstItems、userMap、nameString、itemsList のような名前は使いません。型は型システムがすでに把握しているため、name、usersById、items と書きます。
  9. 辞書に載っている完全な単語を使い、単位や意味も名前に含めます。 maxItemsPerPage、retryDelaySeconds、orderTotalMinorUnits のようにします。max、delay、total のような単独の名前は使いません。略語(opt、qty、amt)は、対応関係をコメントで示したうえで、短い純粋関数の内部に限って使います。単位を省いた名前は、単位に関するバグを招きます。
  10. ブール値は断定文として読めるようにします。 isLoading、hasError、canSubmit、shouldRetry のようにし、loading、error、retry は使いません。条件が文章のように読めるよう、ブール値を返すゲッターやメソッドは is/has/can/should で始めます。
  11. get で始まるアクセサーは使いません。 getDueTasks() ではなく dueTasks を公開します。Dart にはゲッターがあります。関数名には動詞句(loadTasks()、scheduleReminder())を使い、ブール値以外のゲッター名には名詞句(itemCount、nextDueDate)を使います。
  12. インポートはグループ分けして並べ替えます。 dart: を最初にし、次に package:、最後に相対パスを配置します。各グループ内はアルファベット順に並べ、export はインポートの後に独立したセクションとして配置します。dart format と directives_ordering lint に従わせ、フォーマッターに手作業で逆らってはいけません。

接尾辞でレイヤーを示す

役割を示す接尾辞を付けると、名前がレイヤーの契約になります。ファイルを開かなくても、grep やパスを対象としたインポートゲートで、リポジトリ、ビュー、プラットフォーム境界を見分けられます。エンティティを表すトークンには、中立的なドメイン名詞(Task、Order、Account、Item)を使います。

役割接尾辞/パターンレイヤーファイル → シンボル
画面/ルートの対象[Feature]ScreenUI(View)task_list_screen.dart → TaskListScreen
再利用可能なウィジェット[Thing](曖昧な場合のみ Widget を付加)UItask_card.dart → TaskCard
ViewModel(Riverpod、デフォルト)[Feature]NotifierUI(ViewModel)task_list_notifier.dart → TaskListNotifier
ViewModel(Provider の付録のみ)[Feature]ViewModelUI(ViewModel)task_list_view_model.dart → TaskListViewModel
リポジトリ(インターフェース)[Entity]Repositorydatatask_repository.dart → TaskRepository
リポジトリ(実装)Drift[Entity]Repository / Remote[Entity]Repositorydatadrift_task_repository.dart
Drift DAO[Entity]Daodatatasks_dao.dart → TasksDao
定義する機能インターフェース[Concern]Serviceboundaryshare_service.dart → ShareService
サービス実装(フレーバーごと)[Provider][Concern]Serviceapp adapterfirebase_analytics_service.dart → FirebaseAnalyticsService
特定のプラグイン/SDK/ネイティブチャネルのラッパー[Concern]Gatewayboundarynotification_gateway.dart → NotificationGateway
Gateway の実装[Plugin][Concern]Gateway / Live[Concern]Gatewayapp adapterfln_notification_gateway.dart → FlnNotificationGateway
純粋な Dart のドメインロジック[Domain] + 動作を示す接尾辞(Calculator/Validator/Formatter)domainprice_calculator.dart → PriceCalculator
不変モデルドメイン名詞、接尾辞なしanytask.dart → Task
失敗型[Domain]Failure(sealed)anyorder_failure.dart → OrderFailure
  • ViewModel の役割を表すのは Notifier です。型名に ViewModel や ChangeNotifier を決して使わないでください。 Riverpod 3.x では、ViewModel は Notifier/AsyncNotifier/StreamNotifier であり、Screen ごとに 1 つ、1 対 1 で対応させ、[feature]_notifier.dart 内で [Feature]Notifier と命名します(ファイルには主となる宣言を置きます)。[feature]_view_model.dart 内の [Feature]ViewModel が認められるのは Provider/ChangeNotifier の付録に限られます。Riverpod を使う場合には決して使わず、[Feature]Controller も使わないでください。state-management-riverpod を参照してください。
  • Service と Gateway は、どちらもプロバイダーに依存しない境界ポートの名前ですが、契約の所有者によって使い分けます。 [Concern]Service は、あなたが定義する機能インターフェース(ShareService、AnalyticsService)です。[Concern]Gateway は、特定の外部プラグイン/SDK やネイティブの MethodChannel(flutter_local_notifications を利用する NotificationGateway、flutter_secure_storage を利用する SecureStorageGateway)を薄く包むラッパーです。ドメインコードと UI コードではインターフェース名を使い、具体的な実装(FirebaseAnalyticsService、FlnNotificationGateway)はコンポジションルートにのみ置きます。具体的な実装名が共有コードに漏れている場合は、grep で検出できる設計上の問題です。service-boundary-and-native を参照してください。
  • 純粋な Dart の型には、動作を示す Calculator/Validator/Formatter/Parser の接尾辞を付け、Flutter に触れないようにします。 この接尾辞により、その型がフレームワークに依存せず、ウィジェットなしで単体テストできることが分かります。
  • ドメインのユビキタス言語に合わせ、あらゆる場所で一貫性を保ちます。 モデル、リポジトリ、UI 全体で、概念ごとに使う語を 1 つに統一し、検索ですべての参照を見つけられるようにします。

値と時点、および名前で示すその他の区別

型システムでは表せない意味上の区別を、名前に反映させます。安定した暦上の値が必要な場面で生の時点(DateTime.now())を使うのは、よくある正確性のバグです。命名によって境界を明確にできます。

// A real wall-clock instant is legal only at the Clock boundary and named as one.
final DateTime capturedAt = clock.now();      // instant, from package:clock's Clock (never DateTime.now())

// A stored quantity carries its canonical unit in the name.
final int priceMinorUnits;                    // not `price` — unit is explicit
final int distanceMeters;                     // SI base unit, integer

// Unused callback params are `_` (and `__` for a second).
onChanged: (_) => notifier.refresh(),

標準的な保存方法については value-objects-money-and-units を、clockProvider 経由で注入する Clock(package:clock 由来)については service-boundary-and-native を参照してください。独自の ClockService は決して作らないでください。

実例

// task_list_notifier.dart — file name == primary declaration, snake_cased
import 'dart:async'; // dart: group, alphabetized

import 'package:flutter_riverpod/flutter_riverpod.dart'; // package: group, alphabetized

import 'task.dart'; // relative group, alphabetized

/// Frozen paging bound — unit lives in the name (rule 9).
const int maxItemsPerPage = 50; // lowerCamelCase const, not SCREAMING_CAPS

/// ViewModel for [TaskListScreen]. One Notifier per Screen, 1:1 (role suffix).
class TaskListNotifier extends AsyncNotifier<List<Task>> {
  @override
  Future<List<Task>> build() => ref.watch(taskRepositoryProvider).loadTasks();

  /// Verb-phrase command; routes through the repository's single write path.
  Future<void> archive(String taskId) async {
    await ref.read(taskRepositoryProvider).archive(taskId);
    ref.invalidateSelf();
  }

  bool get hasTasks => state.valueOrNull?.isNotEmpty ?? false; // boolean assertion
  int get taskCount => state.valueOrNull?.length ?? 0;          // noun getter, no get- prefix
}

アンチパターン

  • 新しい Dart コードでの SCREAMING_CAPS 定数(const MAX_ITEMS = 50)— lowerCamelCase を使用します。アナライザーで警告されます。
  • クラス名とファイル名が一致しない(home.dart に TaskScreen がある)、または無関係なものを寄せ集めた utils.dart/models.dart — 検索結果からファイルを特定しにくくなります。
  • Flutter に触れる型に …Calculator/…Validator(純粋な Dart を表すサフィックス)という名前を付けたり、Riverpod の ViewModel に …ViewModel/…VM/…Controller という名前を付けたりすること — サフィックスがレイヤーを偽って示すことになります(Riverpod の場合は …Notifier、…ViewModel は Provider の付録内でのみ使用します)。
  • 具象実装の名前(FlnNotificationGateway、FirebaseAnalyticsService)を共有コード/ドメインコードから参照すること — Gateway/Service インターフェースを使い、実装はコンポジションルートに置きます。
  • 安定させるべきフィールドに DateTime.now() を取り込んだり、名前のない時点をドメインロジックに渡したりすること — Clock を注入し、時点に名前を付けます(capturedAt)。
  • getDueTasks() や get/set で始まる名前 — Dart には実際のゲッターがあります。
  • 型を名前に含めること:userMap、nameString、itemsList、strName、iCount。
  • is/has/can/should のないブール値の名前(loading、valid)、単位の分からない数量(total、delay)。
  • 公開シンボルの「名前空間」に先頭の _ を使うこと(プライベートになり、アクセスできなくなります)、l/O/I のような一文字の名前、またはコミットするコードでの data/temp/foo。
  • 単一の短い関数内で対応関係をコメントに記す場合を除き、省略形(opt、qty、msg)を使うこと。
  • インポートグループを手動で並べ替えたり、混在させたりすること — dart format を実行し、directives_ordering に従います。

完了の定義

  • 型は UpperCamelCase、メンバー/変数/定数は lowerCamelCase、ファイル/フォルダー/プレフィックスは lower_snake_case です。
  • ファイル名は主な宣言と一致しています。1 ファイルにつき主な公開型は 1 つです。寄せ集めのファイルやフォルダーはありません。
  • アーキテクチャに沿ったサフィックスを、レイヤーに応じて正しく適用しています(Screen/Notifier/Repository/Dao/Service/Gateway/Failure)。
  • Riverpod の ViewModel は …Notifier です(…Controller/…ViewModel/…VM ではありません)。純粋な Dart の型には Calculator/Validator/Formatter のサフィックスが付き、Flutter のインポートはありません。Service/Gateway インターフェースにプロバイダーは含まれず、実装はコンポジションルートに限定されています。
  • 2 文字を超える頭字語は単語として大文字小文字を使い分けています。辞書に載っている完全な単語を使用しています。単位と意味を名前に含めています。時点には Clock の境界で名前を付けています。
  • ブール値は真偽を表す文として読めます。関数名は動詞句です。ゲッター名は get の接頭辞がない名詞句です。
  • インポートは dart:/package:/相対パスでグループ化し、アルファベット順に並んでいます。dart format と dart analyze --fatal-infos で問題がありません。

関連スキル

  • dart3-idioms-and-coding-standards — 各宣言に適した構文(sealed/record/enum)と、複雑さの制限について。
  • state-management-riverpod — ViewModel が Notifier である理由と、単一の書き込み経路について。
  • service-boundary-and-native — プロバイダーを含まない Service インターフェースと、コンポジションルートに置くその実装について。
  • value-objects-money-and-units — 名前に単位を含めるルールを支える、正規単位での保存について。
  • naming 関連項目:公開シンボルの /// コントラクトについては dartdoc-conventions、directives_ordering/constant_identifier_names の適用については lint-and-style-config、サフィックスが対応するレイヤー DAG については flutter-architecture を参照してください。

参考資料

  • Effective Dart: Style — 大文字小文字の使い分け、ファイル名、インポート順(規範)。
  • Effective Dart: Design — naming — ブール値、ゲッターとメソッドの違い、動詞句/名詞句の表現。
  • Dart linter rules — constant_identifier_names、camel_case_types、file_names、directives_ordering、non_constant_identifier_names。

コメント