ubiquitous-language について
プロジェクトの用語集を管理し、コード、ドキュメント、API、チーム内の会話で命名を統一する開発スキルです。既存用語を先に調べ、用語集やコードの証拠、ユーザーの判断に基づいて新旧の名称や未解決語を扱います。
主な機能
- 01プロジェクト用語の一元管理
- 02命名前の既存用語検索
- 03標準名・禁止名・旧名の区別
- 04定義とコンテキストの記録
- 05用語集の生成と移行
- 06命名監査と履歴の調査
活用シーン
- 01コード要素やAPIの命名
- 02チームのドメイン語彙の統一
- 03コードと用語集の不一致への対応
- 04プロジェクト用語集の点検や更新
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ codealive-ai/ ai-driven-development --skill ubiquitous-languageインストール後、Claude Code で/ubiquitous-languageを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdreferences/generating-thesaurus.mdgit-history-mining.mdnaming-audit.mdscripts/git_term_index.py詳細
ユビキタス言語:プロジェクト用シソーラス管理
ドメイン用語の生きたシソーラスを維持し、名前が必要になるたびに参照することで、コードベース全体で一貫した命名を徹底します。
4 つのモード:
- 命名相談(頻繁)— このファイルのすべての内容
- シソーラス生成(まれ)— references/generating-thesaurus.md を読みます
- 命名監査(定期的)— references/naming-audit.md を読みます
- 履歴のマイニング(任意)—
## Unresolvedの項目について、どの名前が先に使われたか、どの名前がどの名前に置き換えられたか、どの名前が使われなくなりつつあるかの根拠が必要な場合は、references/git-history-mining.md を読みます。生成後、または既存のシソーラスに対する要求に応じて実行できます。
基礎
このスキルは、次の 2 つの知識体系を組み合わせたものです。
- Eric Evans による ドメイン駆動設計(DDD) — ユビキタス言語、境界づけられたコンテキスト、集約の命名
- First Principles Framework(FPF) — 意味の精度を高めるための形式的なツールを提供する、分野横断的な「思考のオペレーティングシステム」です。宣言された意味フレームとしての境界づけられたコンテキスト、多義語の解きほぐし、語彙のファイアウォール、情報損失の注記を伴うコンテキスト間のブリッジ、用語の連続性関係、命名の過剰な増加を防ぐ制御などを扱います。「FPF F.5」や「FPF A.1.1」のような参照は、FPF 仕様の特定のセクションを指します。
核心原則
「プロジェクトでは、共有された単一の語彙を使う必要があります。コード、ドキュメント、API、会話で使われるすべての名前は、シソーラスの用語に対応していなければなりません。概念がシソーラスにない場合は、何かに名前を付ける前に追加してください。」
— ドメイン駆動設計、Eric Evans
コードベースは重要な証拠ですが、自動的に正しいとは限りません。 現在使われている用語を見つけるには、コードを参照します。正規の用語を決めるには、シソーラスとユーザーからの情報を使います。
- 現在存在するものについては、コード(クラス、DB スキーマ、API ルート、イベント)から導き出します
- 標準にすべきものについては、ユーザーまたはドメインエキスパートに尋ねます
- コードが承認済みのシソーラスに反する場合、新しいコードではシソーラスを優先します
- ユーザーが「レガシー命名を修正して」と言った場合、その指示はコードベースより優先されます。既存のコード名を
## Legacyの行に対応付け、ユーザーの用語を正規の用語として使います
暗黙知:まだ実装されていない領域では、最も重要なドメイン知識はアーティファクトではなく、専門家の頭の中にしかありません。
シソーラスファイル
シソーラスの場所を特定する方法:
- ユーザーがパスを指定した場合は、そのパスを使います
- リポジトリ内のどこかに
THESAURUS.mdがすでに存在する場合は、その場所を使います - デフォルト:
docs/THESAURUS.md
ドメイン語彙の信頼できる唯一の情報源です。
バージョン管理
シソーラスでは、フォーマットのバージョンとそれを管理するスキルを YAML
フロントマターで宣言します。機械可読で本文の外にあり、grep 一発で見つけられます。
---
thesaurus-format: "2.0"
skill: ubiquitous-language
---
# Project Thesaurus
バージョンは引用符で囲んでください。引用符のない 2.10 は YAML の浮動小数点数 2.1 になります。
rg -n '^thesaurus-format:' THESAURUS.md→ 1 回でバージョンを確認できます。キーがない場合は 1.0 (スキル 2.0 より前に提供された、インデックスのない文章形式)です。ただし、ファイルにすでに- **Term** `Id` kind:の形式のインデックス行がある場合は、プラグイン 9.2.0 のスタンプなし 2.0 ファイルです。 スタンプを追加してください。- フォーマットのメジャーバージョンは、スキルのメジャーバージョン(このファイルのフロントマターにある
metadata.version)と一致します。スキルのマイナー/パッチリリースではフォーマットは変わりません。 - 自分のものと同じかそれ以下のフォーマットは読み込み、現在のフォーマットのみ書き込みます。メジャーの差がある場合は、先に移行してください(generating-thesaurus.md を参照)。マイナーの差は新しい任意トークンを意味します。古いリーダーでも動作するため、行を編集する際にトークンを追加できます。
- 自分のものより新しいフォーマットのシソーラスは、読み込んでも書き換えないでください。スキルの更新をユーザーに伝えてください。
- ファイルでバージョン管理するのはフォーマットだけです。スキル自体のバージョンは記録しません。編集のたびに古くなり、誰が書いたかは
git logで確認できるためです。skill:はポインターなので、スキルを持たないエージェントでもインストールするものがわかります。
| フォーマット | レイアウト | スキル |
|---|---|---|
| 1.0 | Synonyms to AVOID を含む ### Term 項目、## Legacy Terms 項目、## Forbidden Lexicon テーブル | ≤ 1.x(プラグイン ≤ 9.1.1) |
| 2.0 | grep 優先:kind:/ctx:/avoid: を含む ## Index 行、use: の Forbidden 行、→ の Legacy 行、SKOS ブリッジ | 2.x |
レイアウト:grep 優先
このファイルは、周囲のテキストを読まなくても、名前を rg/grep で 1 回検索すれば「この名前をどう扱えばよいか」がわかるように設計されています。セクションは 5 つあり、順序は固定です。
| セクション | 形式 | 1 行でわかること |
|---|---|---|
## Index | 概念ごとに 1 行 | 「この用語はあるか? 正式名称はどれか? 禁止されている名前はどれか?」 |
## Terms | ### Term 項目 | 「正確には何を意味する/意味しないのか、何と関係があるのか?」 |
## Forbidden | 単語ごとに 1 行 | 「この単語はドメイン名で禁止されているか?」 |
## Legacy | 古い名前ごとに 1 行 | 「この古い名前がコードにある。何に置き換えられたか?」 |
## Unresolved | ### Term — problem 項目 | 「この名前は未解決の問題か?」 |
レジストリの不変条件: プロジェクトで把握している名前はすべて、レジストリ行(Term、Identifier、または avoid としての Index 行、Forbidden 行、Legacy 行、Unresolved 見出し)のいずれか 1 つにのみ記載します。レジストリ行の種類ごとに形式が異なるため、検索結果の形式からステータスがわかり、その行自体から正式名称がわかります。-B/-A による前後の文脈は不要です。
レジストリ行は Markdown テーブルではなく、ラベル付きトークンを含む箇条書きです。テーブルでは |(rg で代替を表す文字)を使う必要があり、列の位置で検索し、フォーマッターによって再整形されます。トークン(kind:、ctx:、avoid:、use:、in:、→)は位置に依存せず、フォーマッターの影響を受けず、エスケープも不要です。
# Project Thesaurus
## Index
- **Order** `Order` kind:aggregate avoid: `Purchase`, `Transaction`, `Buy`
- **Order Line Item** `OrderLineItem` kind:entity avoid: `LineItem`, `OrderItem`, `Item`
- **Order Placed** `OrderPlaced` kind:event avoid: `OrderCreated`, `NewOrder`
## Terms
### Order
- **Definition**: A customer's confirmed request to buy one or more products at agreed prices.
- **NOT**: A payment (that's `Payment`), a shipment, or a draft cart (that's `Cart`).
- **Related**: Order Line Item, Order Placed, Cart
## Forbidden
- `Manager` use: `OrderFulfillment` — hides responsibility; name the activity
## Legacy
- `UserManager` → `Customer` + `CustomerRegistration` in: `src/legacy/` — split in v3
## Unresolved
### Account — one word, two concepts (billing vs auth)
- **Found in**: `billing/Account.ts` (balance), `auth/Account.ts` (login)
- **Question**: Two bounded contexts, or one of them a naming mistake?
- **Impact**: 18 files
- **Options**: `BillingAccount` + `UserAccount`; or contexts Billing / Identity
インデックス行
- **<Term>** `<Identifier>` kind:<kind> [ctx:<Context>] [avoid: `<name>`, `<name>`]
| フィールド | 内容 | ルール |
|---|---|---|
| 用語 | ドメインの専門家が使う呼び名を **太字** で記載 | 複数の単語でも英語以外でもかまいません。また、エントリーの ### ヘッダーテキストにもなります |
| 識別子 | PascalCase のコード形式をバッククォートで囲んで記載 | コード内で検索する対象です。ほかのすべての表記は、これから機械的に導きます(「大文字小文字の使い分け」を参照) |
| kind: | aggregate entity value event command query service role process state policy concept のいずれか | 下記の命名ルールを選びます。該当するものがない場合は concept を使います |
| ctx: | 境界づけられたコンテキスト名 | コンテキストが確定した場合にのみ記載します(generating-thesaurus.md を参照)。その場合、ヘッダーは ### Term (Context) になります |
| avoid: | 禁止する類義語や略語を、それぞれバッククォートで囲み、カンマ区切りで記載 | 可変長のフィールドはこれだけなので、行の最後に記載します。空の場合は省略します |
- 概念ごとに1行です。 その概念のすべての名前をその行に記載します。これにより、逆引き(
rg Purchase→ 「Orderを使用」)で一度に見つけられます。 - 識別子のような名前はすべてバッククォートで囲みます。
rg '`Order`'なら完全一致します。rg OrderではOrderLineItemやReorderもヒットします。rg -F '**Order**'は「用語」に完全一致します。 - 用語のアルファベット順に並べます。
## Terms以下のエントリーも同じ順序にします。 - avoid リストはここにのみ記載します。 エントリーでは繰り返しません。記載場所を1か所にすることで、食い違いを防ぎます。
禁止された名前と旧名称の行
- `<Word>` use: `<Identifier>`[, `<Identifier>`] — <why>
- `<OldName>` → `<Identifier>`[ + `<Identifier>`] in: <files/modules> — <note>
- `<OldName>` → — (see `<X>`, `<Y>`) in: <files> — retired
use: は常にインデックスの識別子を指します。→ は旧名称を示すマーカーです。A + B は旧名称が分割されたことを意味し、→ — は単一の後継がなく、廃止されたことを意味します。
エントリー
### [Term]
- **Definition**: What this concept means in the business domain — one sentence
- **NOT**: What this term does NOT mean; name the neighbouring term it is confused with
- **Related**: Other Index Terms this connects to, written exactly as in the Term field
実際に情報として意味がある場合に限り、任意の行を追加します:**Broader**、**Narrower**、**Part of**、**Has parts**、**Example**。片方の側にのみ記載します。rg で逆方向の情報も取得でき、両側にコピーすると食い違いが生じるだけです。最小限のエントリーは1行です:- **Definition**: … — それ以外の情報はすでにインデックス行に記載されています。
アンカーの文法(検索を正規表現1つで行えるようにするため):ヘッダーは ### <Term> または ### <Term> (<Context>) の形式に厳密に合わせます。それ以外は記載しません。タグ、ステータス、コンテキストの接頭辞はヘッダーではなく、インデックス行に記載します。rg -n '^### Order( \(|$)' でエントリーを検索できます。\b だけでは不十分で、### Order Line Item にも一致します。
シソーラスに記録するのは概念であり、動作ではありません。 名詞(エンティティ名、ロール、プロセス名など)の整理には有効ですが、業務ルールの動作仕様の代わりにはなりません。シソーラスを仕様書にしようとせず、エントリーは短く保ってください。概念に重要な不変条件がある場合は、独立したセクションにせず、定義に簡潔に記載します。
英語以外のドメイン:業務ドメインで英語以外の言語が使われている場合、「用語」には元の言語を使用します。シソーラスには、ドメインの専門家が実際に使う言葉を反映させます。「識別子」にはコード形式を記載します:
- **Счёт-фактура** `Invoice` kind:entity。このために両方のフィールドがあります。
検索プロトコル
名前を新しく考える前に検索してください。これが最も重要な手順です。 命名作業のほとんどで新しい用語は必要ありません。適切な名前はすでに存在しています。
-
場所を特定する:シソーラスを確認します(上記を参照)。なければ、その旨をユーザーに伝え、生成を提案します。
rg -n '^thesaurus-format:'で確認してください。キーがない場合、または値が1.xの場合は旧レイアウトです。以下のプロトコルはプレーンテキスト検索で引き続き機能しますが、最初に一度だけ移行を提案してください。 -
索引を読む:行数が約60行以下なら読みます。概念ごとに1行で語彙全体を網羅しているため、どの検索よりも手間がかかりません。ファイルが大きい場合は、代わりに検索してください。
-
検討中の候補名をすべて検索し、周辺のコードですでに使われている呼び名も検索します。
rg(Grep ツール)またはgrep -Eを使います。同じパターンを使えます。rg -n -i 'invoice' docs/THESAURUS.md # any role, any section rg -n '`OrderLineItem`' docs/THESAURUS.md # exact identifier as seen in code rg -n -F '**Order**' docs/THESAURUS.md # exact Term (not "Order Line Item") rg -n 'avoid:.*`Purchase`' docs/THESAURUS.md # is this word a banned synonym? rg -n 'kind:event' docs/THESAURUS.md # all terms of one kind rg -n 'ctx:Billing' docs/THESAURUS.md # everything one context owns rg -n '`Basket` →' docs/THESAURUS.md # legacy name and its replacement rg -n -A4 '^### Invoice( \(|$)' docs/THESAURUS.md # the entry itself注意点は1つだけです。
*と|は正規表現のメタ文字です。**Term**には-Fを使い、テーブルのパイプは(存在しないため)検索しないでください。 -
見つかった行の形式に応じて対応する:
見つかった行の形式 意味 対応 - **X** `X` kind:…— 対象の単語が Term または Identifier概念が存在する Identifier をそのまま使います。ここで終了です - **X** … avoid: … `your word`使おうとしていたのは使用禁止の同義語です その行の Identifier を使います - `word` use: …その単語はドメイン名では使用禁止です use:の後にある Identifier を選びます- `word` → …古い名前がまだコードに残っています 新しいコードには →の後にある置換後の名前を使い、古い名前を広めないでください## Unresolvedの下にある### word — …命名に関する未解決の質問 黙って決めず、問題を提示してユーザーに尋ねるか、履歴の調査を提案します(下記参照) ###の見出し/エントリ本文のみ関連する概念 エントリを読み、名前の組み立てに役立てます 該当なし 新しい概念 「概念が新しい場合」へ進みます -
索引の行に
ctx:がある場合は、範囲が限定されたコンテキストを確認します。同じ単語でも、コンテキストによって正式な名称にも使用禁止の名称にもなり得ます。
必ず、次のものに名前を付ける前にこの手順を実行してください。クラス、インターフェース、型、列挙型、集約、エンティティ、値オブジェクト、関数、メソッド、コマンド、クエリ、ドメインイベント、変数、定数、フィールド、パラメーター、DB のテーブル/列/コレクション、API エンドポイントおよびレスポンスフィールド、ファイル、ディレクトリ、モジュール、パッケージ、機能フラグ、設定キー、環境変数、ドメイン概念に言及するコミットメッセージや PR タイトル。
概念が新しい場合
新しい用語を作る前に、次の4つの方法を試してください(FPF F.14「名前を減らし、表現を豊かに」より)。
- 再利用 — 既存の用語で表せないでしょうか。新しい概念ではなく、既存概念のバリエーションかもしれません
- 組み合わせ — 既存の用語を組み合わせられないでしょうか。
OrderLineItemはOrderとLineItemを再利用しています - 限定 — 状態や時間帯が異なるだけの同じ概念ではないでしょうか。
NightOperatorを新しく作るのではなく、Operatorに時間の限定を加えてください - 尋ねる — それでも不明な場合は、「[概念]に名前を付ける必要があります。シソーラスにこの用語はありません。ドメインでは何と呼んでいますか?」と尋ねてください。
ユーザーにも答えがない場合 — それは行き止まりではなく、空白領域です。
ユビキタス言語の構築は、抽出ではなく共同作業です。見出しに
[WHITE-SPOT]タグを付けて## Unresolvedに追加してください。定義されていない概念に、無理に名前を付けないでください。
4つの方法をすべて試しても解決しない場合に限り、新しい用語を作ります。
- 不変条件によって真となることに名前を付ける(FPF F.5) — 願望で名前を付けないでください。コードが「Premium」を保証していないなら、
PremiumCustomerとは呼ばないでください - 必要最小限の一般性を選ぶ — 実際に適用するルールを表せる中で、最も範囲の狭い名前を選びます。普遍的に聞こえるように
TaskをActivityに格上げしないでください - 1~3語に収める — 修辞的な形容詞(「堅牢な」「最適な」「高度な」など)は避けます
- シソーラスに追加する:索引の行(Term、Identifier、
kind:、avoid:。空の場合はavoid:を省略)と、少なくとも Definition を含む### Termエントリの両方を追加します。どちらもソート順を保ってください - その後、コードでその用語を使います
不整合を見つけた場合
既存のコードで類語辞典と矛盾する用語が使われている場合:
- 指摘します:「
fetchPurchases()を見つけましたが、Index の行にはOrderと記載され、avoid:の下にはPurchaseがあります」 - 影響範囲が小さい場合は、名前の変更を提案します
- 大規模な名前変更の場合は、技術的負債として記録し、どのように進めるかユーザーに確認します
曖昧さが自然に解消されない場合
名前が ## Unresolved に入る場合――1つの概念に2つの綴りがある、1つの綴りに2つの概念がある、明確な優先候補がない――現在のツリーだけでは判断できませんが、リポジトリの履歴から分かることがよくあります。どの識別子が先に生まれたのか、どのコミットで一方が削除されて他方が追加されたのか、どちらが廃止されつつあるのか、といったことです。
黙って実行せず、提案してください。 ## Unresolved の項目を提示する際は、生成後、監査後、または命名に関する疑問が生じたときのいずれも、次の提案を一度だけ行ってください。
「これらについて git の履歴を調べ、各名前がいつ生まれ、どちらがどちらに置き換えられ、どちらが増加または減少しているかを確認できます。リポジトリ外に一時インデックスを作成し、調査後に削除します。 手作業で回答する前に試してみますか?」
.git が存在しない場合、履歴が約50コミット未満の場合、またはインポート時に履歴が squash されている場合、あるいは項目が [WHITE-SPOT] タグの場合(名前のない概念は履歴に痕跡を残さないため)は、提案を省略してください。
ユーザーが同意した場合は、references/git-history-mining.md を読み、手順に従ってください。概要は次のとおりです。
S=<skill-dir>/scripts/git_term_index.py
python3 $S build --repo-dir . --content # throwaway SQLite index in $TMPDIR, never in the repo
python3 $S query Account Customer # birth, dormancy, trajectory, renames, messages
python3 $S pair User Customer # competing names: birth order + swap commits
python3 $S contexts Account # where the name lives — the polysemy check
python3 $S search 'rename account' # BM25 search over commit messages
python3 $S clean # delete the index when done
インデックスは差分履歴全体を対象とするため、「誕生」は本当に誕生を意味します。大規模なリポジトリでは --pathspec src/ を使ってください。ビルド時間を短縮し、同時にシグナルの精度を高められます。
pair はラベル付きの判定――RENAME (strong / probable / possible)、DRIFT、NOT A RENAME、COEXISTENCE――を出力します。方向は引数の順序ではなく、証拠から推定されます。出力されたラベルをそのまま報告し、格上げしないでください。 「RENAME — strong」は、コミットが名前を入れ替え、さらにコミットの説明がそれを明示していることを意味します。それより確度の低いものは、まず git show で確認してください。
多義性(1つの単語に2つの意味があり、2つのモジュールにまたがる場合)には、パスの分割を使います。pair は files: A in N, B in M, both in K を出力し、contexts <name> は1つの単語についてディレクトリごとの内訳を出力します。both in 0――両方が同じファイルに含まれたことがない場合――は、境界が明確な2つのコンテキストが存在することを示す実質的な証拠です。ファイルが共有されている場合は同義語の変遷を示しており、履歴だけでは判断できません。ツールがパスの分割を出力した場合に限り、パスの分割があると述べてください。 ファイルのみの名前変更(ファイルが移動したが、識別子は変わっていない)は pair ではなく、query のファイル名変更セクションに表示されます。両方を実行してください。
履歴は証拠であり、絶対的な判断基準ではありません。 履歴は候補に順位を付け、コミットを示しますが、決定するのはユーザーです。提案は確信度とともに一括で報告し、承認されたものだけを適用します。適用した決定それぞれについて、その根拠となったコミットを記録してください(Legacy 行に — renamed in a41f2c9 と記すか、エントリーに - **History**: 行を追加します)。
DDD 構成要素ごとの命名規則
Index の Kind 列でルールを選択します。
集約と集約ルート(aggregate)
ビジネスドメインの用語を使います。単数形にします。技術的な接尾辞は付けません。
GOOD: Order, Invoice, UserAccount, ShoppingCart
BAD: OrderAggregate, OrderRoot, OrderAggregateImpl, OrderEntity
エンティティ(entity)
ドメインに由来する単数形の名詞を使います。識別子を持つものです。
GOOD: OrderLineItem, PaymentTransaction, Customer
BAD: OrderLineItemEntity, OrderLineItemImpl, OrderLineItemObj
値オブジェクト(value)
不変の概念を表す単数形の名詞を使います。何をするかではなく、何であるかを表します。
GOOD: Money, Email, PhoneNumber, Address, DateRange
BAD: MoneyValue, EmailValidator, PriceInfo, AmountData
ドメインイベント(event)
過去形の動詞+名詞にします。発生した出来事を表します。
GOOD: OrderPlaced, PaymentCaptured, InvoiceSent, InventoryReserved
BAD: OrderEvent, OnOrderPlaced, CreateOrder (that's a command)
コマンド(command)
命令形の動詞+名詞にします。要求されたアクションを表します。
GOOD: CreateOrder, CancelInvoice, ProcessRefund, ReserveInventory
BAD: OrderCreated (that's an event), NewOrder, OrderCommand
クエリ(query)
質問または取得を表します。動詞+目的語、または内容を表す名前にします。
GOOD: GetOrderById, FindInvoicesByCustomer, ListPendingOrders
BAD: RetrieveOrderData, OrderQuery, GetterForOrder
ドメインサービス(service)
ドメインの専門家が認識できるビジネス活動にちなんだ名前にします。
GOOD: InvoiceCalculator, OrderFulfillment, NotificationSender
BAD: OrderManager, GenericService, HelperService
リポジトリ
リポジトリという接尾辞は使用できます。これはインフラストラクチャのパターンです。リポジトリは類語辞典の用語ではありません。保存する集約の名前を使います。
GOOD: OrderRepository, InvoiceRepository, CustomerRepository
BAD: OrderStorage, OrderPersistence, OrderFinder, OrderDao
集約のメソッド
コマンド(状態を変更する): 命令形の動詞を使い、「Get」接頭辞は付けません。
GOOD: order.Cancel(), order.AddLineItem(product, quantity), order.Recalculate()
BAD: order.CancelOrderMethod(), order.GetCancelled(), order.DoCancelOrder()
クエリ(読み取り専用): Get、Is、Has、Can、またはドメイン動詞で始めます。
GOOD: order.GetTotal(), order.IsExpired(), order.CanBeShipped()
BAD: order.FetchInfo(), order.CheckData()
検出して指摘する命名アンチパターン
語彙のファイアウォール:## Forbidden セクション
ドメイン層は、一時的な専門用語、曖昧な用語、実装の詳細から保護する必要があります。シソーラスの ## Forbidden セクションには、ドメイン名に使ってはならず、必ず具体的なドメイン用語に置き換えるべき語が記載されています。
曖昧な語(ドメイン層では使用しない)
| 曖昧な語 | 問題点 | 修正方法 |
|---|---|---|
Info | 意味のない接尾辞です | 削除します:UserInfo -> User |
Data | 概念について何も説明していません | ドメイン用語を使います:OrderData -> Order |
Manager | 曖昧で、責任範囲が隠れます | 実際の責任に基づいて分割します |
Handler | 汎用的で、意図が不明確です | 処理する対象にちなんだ名前を付けます |
Service | 何でも引き受ける名前として使われすぎています | 具体的なドメイン上の活動名を使います |
Base | 技術的なノイズになります | 削除し、コンポジションを使います |
Item | 汎用的すぎます | ドメイン用語を使います:Item -> OrderLineItem、Product |
Util / Helper | 設計に問題があることを示します | ロジックをドメインオブジェクトに移します |
Object / Obj | 適切な場面はありません | 接尾辞を削除します |
Record / Model | データベースの概念がドメインに漏れています | ドメイン用語を使います |
Config / Settings | 概念を隠す汎用的なコンテナーです | Config -> LoanProduct、Settings -> NotificationPreferences |
ドメイン層の技術用語
ドメインコードに実装の詳細を含めてはいけません:
BAD: MongoOrder, SqlUserRepository, HttpOrderService, OrderDto, OrderEntity
GOOD: Order, OrderRepository (interface), PaymentGateway, Order (just Order)
技術的な接頭辞・接尾辞はインフラストラクチャ層でのみ使用し、その場合もドメイン上の役割を先に示します:
INFRASTRUCTURE LAYER (OK): MongoOrderRepository, RedisSessionCache, HttpPaymentClient
DOMAIN LAYER (NEVER): MongoOrder, RedisSession, HttpPayment
NAME THE ROLE, NOT THE TECH: SessionStore not RedisCache, EventPublisher not KafkaProducer
フレームワークに関する注意点:ドメインと永続化を意図的に融合するフレームワーク(Active Record パターンや ORM 中心のフレームワーク)では、モデル自体がドメインエンティティです。ドメインを表す名詞は簡潔に保ち、フレームワークとの結合はクラス名ではなく、継承、アノテーション、メタデータに持たせてください。技術用語がビジネス向けの名前に含まれる場合や、境界の外に漏れ出す場合に限り、問題として指摘してください。
同義語の揺れ
コードの異なる箇所で、同じ概念が別の名前で呼ばれています:
PROBLEM: "Customer" in auth, "User" in API, "Account" in billing — all mean the same thing
FIX: Pick ONE canonical term per bounded context. Put the others in that line's `avoid:` list.
略語の境界
長期間使われ、ドメインを表す名前では略語を禁止します:型、エクスポート関数、モジュール、API フィールド、DB カラム、イベント、設定キー。
スコープ内で意味が明確な場合は、慣例的で短命なローカル識別子を許可します:
i、j、ctx、req、res、err、tx、db、イベントを表すe。
主要な用語として定着している場合は、業界標準の頭字語を許可します:SKU、VAT、URL、ID、OAuth。専門家が頭字語を使う場合に、無理に不自然な展開形を使わせないでください。
PROBLEM: usr, user, account, acct — competing abbreviations for the same durable concept
FIX: Pick ONE canonical form for domain-bearing names. Short-lived locals are exempt.
翻訳の連鎖(「伝言ゲーム」)
知識の連鎖において、同じ概念に対して成果物ごとに異なる用語が使われると、翻訳のたびに情報が失われます:
SMELL: Domain expert says "Campaign" → PM writes "Promotion" in spec →
Dev codes `marketing_push` → QA tests "advertising effort"
FIX: Same term everywhere: expert, PM, dev, QA all say and write "Campaign"
これは同義語の揺れよりも深刻です。翻訳のたびにニュアンスやビジネスルールも失われるためです。検出方法: 要件書、仕様書、チケットで使われている用語とコード上の名前を比較してください。一致しない場合、ユビキタス言語に翻訳の断絶があります。ドメインエキスパートが使う用語を、どこでも採用してください。
表記規則
インデックスの Identifier は PascalCase です。それ以外の形式はすべて、プロジェクトの規則に従って機械的に派生させます。言い換えはしません。
| コンテキスト | 規則 | 例(Identifier:OrderLineItem) |
|---|---|---|
| クラス/型 | PascalCase | OrderLineItem |
| 関数/メソッド | プロジェクトの規則 | addOrderLineItem / add_order_line_item |
| 変数 | プロジェクトの規則 | orderLineItem / order_line_item |
| 定数 | UPPER_SNAKE | MAX_ORDER_LINE_ITEMS |
| データベーステーブル | プロジェクトの規則 | order_line_items |
| API エンドポイント | kebab-case または規則に準拠 | /orders/{id}/line-items |
| イベント/メッセージ | 過去形の動詞を含む PascalCase | OrderLineItemAdded |
| ファイル/ディレクトリ | プロジェクトの規則 | order_line_item.py、OrderLineItem.cs |
主なルール:
- Identifier をそのまま使います。省略(
ord)、拡張(orderObject)、同義語への置き換え(purchase)はしません。 - 複合名は Identifier を組み合わせます。
PurchaseLineItemではなく、OrderLineItemです。 - インフラストラクチャの役割を示す技術的な接尾辞は使用できます。
OrderRepository、OrderDTO(インフラ層のみ) - 複数語の Identifier は、すべての表記形式で全単語を維持します。
ProcessingStage→processing_stage。proc_stageとはしません。 - 変数とパラメーターには、省略形ではなく完全で説明的な名前を使います。
amtではなくtotalAmount、cEmailではなくcustomerEmailです。
シソーラスの更新
用語を変更するときは、事実を正確に伝える範囲で最も強くない関係を使います(FPF F.13 に基づく)。
| 操作 | 条件 | シソーラスへの影響 |
|---|---|---|
| 追加 | 新しい概念 | インデックス行とエントリーを追加します。最低限必要なのは、Identifier、kind:、定義です。 |
| 改名 | 意味は変えずに表現を改善する | インデックス行と見出しの Term/Identifier を変更します。古い Identifier は ## Legacy 行に `Old` → `New` と記載します。コードベースを検索し、改名を提案します。 |
| 分割 | 1 つの用語が 2 つの意味を表していた | 古い行を削除し、2 つの新しい行とエントリーを追加します。古い Identifier は ## Legacy 行に `Old` → `A` + `B` と記載します。それぞれの NOT で意味を区別します。 |
| 統合 | 2 つの用語が実際には同じ意味を表している | 片方の行を残し、もう一方の Identifier をその avoid: リストに移します。エントリーを統合します。 |
| 廃止 | 用語が誤解を招き、後継となる用語が 1 つに定まらない | ## Legacy 行に `Old` → — (see `X`, `Y`) と記載します。 |
| 非推奨化 | 概念を段階的に廃止している | 置き換え先を示す → と in: の場所を含む ## Legacy 行を追加します。 |
重要な確認事項:変更の前後で同じ概念を指していますか?
- はい。概念は同じで、表現が改善された場合 → 改名(古いコードを読むときのレガシー別名として残します)
- いいえ。概念自体が変わった場合 → 分割または統合(改名ではありません)
別名の最小化:各用語のレガシー別名は最大 1 つにします。古いコードで読者が最もよく目にするものを選んでください。編集後もレジストリの不変条件を維持します。名前は avoid: の下か Legacy のどちらか一方にのみ置き、両方には置きません。
古いレイアウトの場合thesaurus-format のフロントマターキーがない場合(または 1.x の場合)は、インデックス導入前の文章レイアウトです。
references/generating-thesaurus.md の「既存のシソーラスの移行」を参照してください。
名前を付ける前の簡易チェックリスト
- この名前とその同義語を類語辞典で検索しましたか?検索結果はどこにありましたか?
- この分野の専門家がこの名前を見て、意味を理解できますか?
- 曖昧な単語(Manager、Service、Handler、Info、Data、Item、Base、Util)が含まれていませんか?
- 抽象的すぎませんか(異なる文脈で複数の意味に取れませんか)?
- インフラストラクチャの詳細(Mongo、Sql、Http、Dto、Entity、Model)が露呈していませんか?
- コードベース全体で、この用語のほかの使い方と一貫していますか?
- インデックスにある正確な識別子を使っていますか?それとも、
avoid:リストにある同義語を使っていませんか? - この用語に適した境界づけられたコンテキスト内にいますか?
- 名前が種類と一致していますか?
eventなら過去形、commandなら命令形になっていますか? - この名前が何を表すのか、ドメイン用語を使って一文で説明できますか?
- 新しい概念の場合、使用する前にインデックスの行と項目を追加しましたか?
いずれかの回答に懸念がある場合は、作業を続けずに修正してください。