本文へ移動
cccskills
無料GitHub で公開

domain-design-safety-discipline

ドメインモデル、可視性、操作可能性、不変条件、層境界、API 設計に関わる実装時に使う。不正な状態や誤った呼び出し方を、型、関数の境界、ドメイン語彙で表し、正しい使い方が自然になる設計へ寄せるためのスキル。

インストール方法を見る

含まれるファイル(1)

  • SKILL.md12.7 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

安全なドメイン設計の規律

基本方針

正しい使い方が自然になり、誤った使い方が書きにくい設計を優先する。

呼び出し側の注意力、コメント、ドキュメント、テストだけに依存しない。暗黙の順序、事前チェック、検証済みであることは、可能な限り型、関数、戻り値、境界で表現する。

型と境界を変える前の確認

型と境界の齟齬は、実装後に直すほど高くつく。型・境界・責務を追加・削除・変更する前に確認する。

集約は強整合性の単位で決める

集約は、意味のあるデータのまとまりであり、強整合性が要求される単位である。

  • 集約境界を決める前に、強整合性が必要な操作を列挙し、そこから境界を導く
  • 保存の都合から境界を導かない。別テーブルや別 map に分けて保存できることは、整合性の単位を分けてよい理由にならない。保存方法と整合性境界は別の問い

役割に必要な情報を持てるか

概念に役割(判定、検証、変換、状態遷移の起点など)を担わせる前に、その概念が役割の遂行に必要な情報を自分の境界内で持てるか確認する。

持てない場合、その場で代替案を出し始めず、選択肢を並べて比較する: 必要な情報を持つ別概念を立てる / 境界を引き直す / 役割を担う仕組みの側を変える / 仕様上の制約として記録して許容する。

名前と意味を分ける

同じ名前は同じ意味を保証しない。操作や型を「何が、何に対して、何を起こすか」の自然言語で言い直し、同名同士で意味が一致するか確認する。

  • 同じ操作名が、対象や粒度の違う操作(対象そのものへの操作と、その配下の要素への操作など)を指していないか
  • 追加・改名する型は、新しいドメイン概念か、既存概念の別名か

型の役割を 1 つに言い切る

型を追加・削除・改名する前に、その型の役割を 1 つ選ぶ: 永続化される対象 / 操作の前提を表す値 / 判定のためだけの一時的な入力 / 処理途中の作業データ / 層境界の受け渡し用。

複数に当てはまる、またはどれとも言えないなら、概念が混ざっている。判定のためだけの一時的な値を domain / repository 境界に出す場合は、設計として妥当か疑う。

正しい使い方が自然になる API

避けるもの:

  • 特定の順番で呼ばないと壊れる API
  • 呼び出し前に別の関数でチェックが必要な API
  • 不正な状態を作れてしまう struct / enum
  • bool や primitive の組み合わせから意味を推測させる API
  • コメントで注意しないと危険な public method

考えること:

  • 事前チェックを生成関数や constructor に寄せられないか
  • 戻り値で次に可能な操作を制限できないか
  • enum の variant で状態を分け、不正な組み合わせを消せないか
  • private field と public constructor で不変条件を守れないか

Always-valid domain model

ドメイン上ありえない状態は、可能な限り型の上で作れないようにする。

  • 検証を handler や usecase に散らさない
  • 値オブジェクト、集約、生成関数、domain service に不変条件を寄せる
  • String / Vec / primitive 型をそのまま使う前に、ドメイン制約を持つ型にできないか考える
  • 「作った後にチェックする」より、「チェック済みの値しか作れない」形を優先する
  • ただし、意味の薄いラッパー型や、1 箇所でしか価値のない型を増やしすぎない

判断基準:

  • その型は不正状態を実際に消しているか
  • その型名はドメイン語彙として意味があるか
  • その検証は複数箇所へ散らばる可能性があるか
  • テストや呼び出し側の注意を減らしているか

型の配置と凝集度

型をどこに定義するかは、型の種類ではなく、ドメイン上の所有者で決める。

値オブジェクトや ID 型は、それを構成要素として持つ集約と同じモジュールに置く。UserId は User 集約の一部であり、OrderId は Order 集約の一部である。「identifier である」という形の類似は、まとめて置く理由にならない。

避けるもの:

  • ID 型を identifiers のような 1 つのファイルへまとめて定義する
  • types / values / newtypes など、型の種類だけを共通点にしたモジュール
  • 他の集約から参照されるという理由だけで、型を共有モジュールへ移す
  • 小さな型が集約のそばに並ぶのを嫌って、整理のために種類別ファイルへ寄せる

考えること:

  • その型は、どの集約・どの概念の一部として説明できるか
  • 集約間の参照は ID で行い、ID 型の定義は所有する集約側に置いたまま、参照する側が import できないか
  • モジュールを分けるなら、同じ理由で変更されるものが近くに集まる分け方になっているか
  • 依存の向きが気になる場合も、共有モジュールへ逃がす前に、所有者のそばに置いたまま解決できないか

ドメイン語彙で説明できるか確認する

設計判断や条件分岐を置く前に、それをドメインの言葉で説明できるか確認する。

技術的な都合の言葉だけで説明している場合、ドメイン上の概念やルールを見落としている可能性がある。status、flag、type、mode などの汎用的な名前に寄せる前に、その区別がドメイン上は何を意味するのかを考える。

考えること:

  • その条件は、業務上・プロダクト上は何と呼ばれるものか
  • 利用者、運用者、ドメインの専門家に説明するとき、どの語を使うか
  • 技術的な状態名ではなく、ドメイン上の状態や段階として名前を付けられないか
  • 「更新する」「無効にする」「許可する」ではなく、ドメイン上は何が起きているのか
  • 実装都合の分類が先に立って、ドメイン上の重要な区別を潰していないか
  • ドメインの言葉で説明できない場合、それは本当に domain 層へ置くべき概念か

可視性・操作可能性

可視性や操作可能性は、最初から application 層の手続きとして扱うものとは限らない。

「誰が見えてよいか」「誰が操作してよいか」「どの状態なら操作できるか」は、単なる外側の制御ではなく、ドメイン仕様であることが多い。handler や usecase に条件分岐を書く前に、それがドメインの言葉で説明できるルールかを確認する。

避けるもの:

  • handler / controller だけで、可視性や操作可否の判断を完結させる
  • usecase の末端で、取得結果を削るだけにする
  • presentation 層に「見えてよいか」「操作してよいか」の判断を漏らす
  • 可視性や操作可否の漏れをテストだけで防ごうとする
  • 取得後に filter するだけで、本来取得してはいけないデータを扱う

考えること:

  • そのルールは application 層の制御か、ドメイン上の制約か
  • ドメイン仕様なら、集約、domain service、policy、query model のどこで表現するのが自然か
  • 操作可能な状態と操作不能な状態を、型や戻り値で区別できないか
  • repository / query の段階で可視性条件を表現すべきか
  • 操作の前提条件を呼び出し側の記憶に頼らない API にできないか

ドメイン語彙で操作を表す

ドメイン上の操作は、できるだけドメインの言葉で表現する。

望ましい例:

  • post_message
  • submit_answer
  • publish
  • archive
  • invite_member
  • mark_as_read

避けたい例:

  • update_xxx_field
  • set_status
  • modify_record
  • apply_flag

外側の usecase に手続きが散らばっている場合、その操作を集約や domain service のメソッドとして表現できないか考える。

ただし、巨大な集約を無理にすべてロードしてまで、自然なメソッドの形にしない。整合性境界と性能上の境界は分けて考える。

層境界と型の流出

境界を越える型を混ぜない。

  • infra の型を domain / application に漏らさない
  • DB entity、HTTP request / response、外部 API DTO は境界で変換する
  • usecase を presentation の都合に合わせたレスポンスの形へ寄せすぎない
  • presentation 層にドメイン判断、可視性判断、永続化都合を漏らさない
  • mapping は面倒でも、境界を明確にするために分離する

判断基準:

  • この型はどの層の都合を表しているか
  • 外部 API や DB schema の変更が domain に波及しないか
  • presentation の表示都合が usecase や domain に入り込んでいないか
  • mapping を省いたことで、境界が曖昧になっていないか

差分レビュー

実装後、差分に対して行う。差分と要求だけを渡された第三者でも同じ結果になるように、対象を列挙してから、対象ごとの問いに答える形で進める。誰がこのレビューを行うかは、このスキルでは決めない。実装の進め方を管理する側に従う。

手順:

  1. 差分から対象を列挙する: 新規・変更した型 / 新規・変更した public 関数・メソッド / 層境界をまたぐ箇所 / 追加・変更した集約境界。
  2. 対象ごとに下の問いに答える。「問題なし」で済ませず、根拠を 1 行で言い切る。言い切れない対象は、本文の該当節へ戻って設計から見直す。
  3. 問題が見つかった対象は修正し、修正した対象にもう一度この手順を適用する。

型ごとの問い:

  • 所有者: どの集約・どの概念の一部として説明できるか。定義は所有者と同じモジュールに置かれているか
  • 役割: 「型の役割を 1 つに言い切る」の 5 分類のどれか 1 つで言い切れるか。役割の遂行に必要な情報を自分の境界内で持てているか。判定のためだけの一時的な値が domain / repository 境界に出ていないか
  • 不変条件: どこで守られているか。生成関数・constructor・private field のどこでも守られていないなら、不正な状態を作れる型になっていないか。検証が handler / usecase 側に書かれていないか
  • 存在意義: その型は不正状態を実際に消しているか。意味の薄いラッパー型を増やしていないか

public 関数・メソッドごとの問い:

  • 呼び出し側が順序や事前チェックを覚える必要があるか。あるなら「正しい使い方が自然になる API」へ戻る
  • 名前はドメイン語彙か。set_ / update_ / modify_ 系の名前なら、ドメイン上は何が起きているのかを言い直す
  • 同名の操作が、対象や粒度の違う別の意味を指していないか

層境界をまたぐ箇所ごとの問い:

  • 越えている型は、どの層の都合を表しているか。DB entity / HTTP request・response / 外部 API DTO が domain / application に入っていないか
  • 可視性・操作可否の判断はどの層にあるか。handler / presentation だけで完結していないか。ドメイン仕様なら domain 側で表現されているか

集約境界を追加・変更した場合の問い:

  • 境界は強整合性が必要な操作から導いたか。保存の都合や性能上の都合から導いていないか

機械的に確認できる項目(対象の列挙とあわせて確認する):

  • identifiers / types / values のような、型の種類でまとめたファイルを新設していないか
  • domain 層のシグネチャに String / bool / primitive の引数・戻り値が増えていないか
  • status / flag / type / mode のような汎用名のフィールドや分岐が増えていないか。増えているなら、ドメイン上の何の区別かを言い直す

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

add-npm-package

無料日本語概要

modules/npm/packages/ に新しい npm/pnpm パッケージを追加するスキル

rito528/dotfiles22026年10月12日 更新

add-script

無料日本語概要

install/ 配下に新しいセットアップ用シェルスクリプトを追加するスキル

rito528/dotfiles22026年10月12日 更新

create-issue

無料日本語概要

GitHub issue を新規作成・下書きするとき、または既存 issue の本文を書き直すときに必ず使うスキル。「issue を作って」「issue 化して」「これを issue にまとめて」のような依頼で発火し、`gh issue create` を直接呼ぶ前に必ず参照する。人間が合意した内容だけを、意味を足さずに issue へ残すための基準を定める。

rito528/dotfiles22026年10月12日 更新

create-pr

無料日本語概要

GitHub に Pull Request を作成するスキル。ユーザーが「PRを作って」「プルリクエストを出して」「PR作成して」と言ったとき、またはコミット済みの変更をレビューに出したいときに使用する。

rito528/dotfiles22026年10月12日 更新

ensure-branch

無料日本語概要

コミットやPR作成の前に、作業ブランチが適切かどうかを確認・切り替えるスキル。git-commit や create-pr からサブルーチンとして呼び出される。

rito528/dotfiles22026年10月12日 更新

functional-core-style

無料日本語概要

コードを実装・修正・リファクタリングするすべてのタスクで使うスキル。純粋関数を中心に設計し、副作用(DB、HTTP、ファイル、時刻、乱数、ログなど)を境界へ寄せ、データ変換、エラー表現、コレクション操作、可変状態の扱いを既存コードに合う形で整理する。ユーザーが明示的に指示しなくても、言語を問わず、コードを書き始める前に発火する。設定値やドキュメントだけの変更では不要。

rito528/dotfiles22026年10月12日 更新

rito528 のスキルをすべて見る

このスキルの問題を報告する