リポジトリがドメインに依存しすぎた話

題材 リポジトリがドメインに依存するのはありか? ドメインを知りすぎた実装 ドメインを知りすぎない実装 今回は、リポジトリがドメインに依存しすぎた話を書きたいと思います。 題材 題材は、口座振替管理アプリで、ローカルの DB にデータを保存する場合の話です。 支払情報編集画面という画面があり、この画面では、データの新規登録と既存データの更新が可能になっています。 この「新規登録」処理と「更新」処理を行う際に、リポジトリはどこまでドメインを知っていていいのか?という話になります。 リポジトリがドメインに依存するのはありか? まず、クリーンアーキテクチャ的には、リポジトリがドメインに依存することは間違ってはいません。 ただし、どこまで知っていてよいのかは検討の余地があるというのが今回の学びです。 ドメインを知りすぎた実装 まずは、ドメインを知りすぎた良くない実装を紹介します。 以下は、支払情報を表す Payment オブジェクトです。 sealed interface Payment { val name: PaymentName val payerId: PayerId data class Persisted( val id: PaymentId, override val name: PaymentName, override val payerId: PayerId = PayerId.NONE, ) : Payment data class InMemory( override val name: PaymentName, override val payerId: PayerId = PayerId.NONE, ) : Payment } 編集の場合は、すでにデータが永続化されているため、 Persisted オブジェクトで表現します。永続化済みのデータは ID を持っています。 新規登録の場合は、 ID を持っていないため InMemory オブジェクトで表現します。 リポジトリ側には、この Payment オブジェクトが渡され、このオブジェクトの実態が Persisted なのか、 InMemory なのかによって、新規作成か、更新かを分けています。 ...

April 2, 2026 · 1 分 · 奥田 智紘

Jetpack Compose Navigation のパラメータ指定の罠

すぐに思いつく実装 「新規作成 or 編集」を型で表現する なぜエラーになるのか 正しい設計:Navigation はプリミティブ型を渡す 重要なポイント Navigationの責務 UI の責務 一覧画面から「新規作成」と「編集」の両方に遷移するケースは、アプリ開発でよくあります。 例えば以下のような仕様です: リストのアイテムをタップ → 編集画面へ 「追加」ボタンをタップ → 新規作成画面へ 画面UIは同じ(内部の動作だけ異なる) すぐに思いつく実装 すぐに思いつく実装方法は以下ではないでしょうか? Navigation の定義は以下の通り NavHost( navController = navController, startDestination = startDestination, modifier = modifier, ) { // リスト画面 composable<ItemList> { PayeeListScreen( onClickAdd = { navController.navigate(RegisterItem(null)) }, onClickItem = { itemId -> navController.navigate(RegisterItem(itemId)) } ) } // 登録・編集画面 composable<RegisterItem> { backStackEntry -> val registerItem: RegisterItem = backStackEntry.toRoute() RegisterItemScreen( onClickNavigateUp = { navController.navigateUp() }, itemId = registerItem.id ) } Destination の定義は以下の通り ...

March 22, 2026 · 1 分 · 奥田 智紘

ワイヤーフレームからドメイン設計が進んだ話

はじめに ドメイン設計だけでは埋まらない違和感 ワイヤーフレームを作って起きた変化 1. ユーザーの行動が具体化される 2. 必要なデータと制約が見えてくる 3. ドメイン設計の「抜け」が露出する ワイヤーフレームは見た目のためではない まとめ おわりに はじめに 最近、アプリ開発の中で「ワイヤーフレーム」を使い始めたところ、思いがけずドメイン設計が大きく前進しました。 ワイヤーフレームとは、 Figma などを使って作る UI デザインの一種です。 ただし、色やフォントサイズなどの細かい UI にはこだわりません。 ボタンや入力欄であることがわかれば良いという程度のラフな UI デザインのことを言います。 もともとドメイン駆動で設計を進めていたのですが、どうしても「抜け」や「曖昧さ」が残る感覚があり、手応えが弱い状態が続いていました。 しかし、ワイヤーフレームを作り始めたことで、その状況が一変しました。 この記事では、 ワイヤーフレームがどのようにドメイン設計を補完し、加速させたのかを整理してみます。 ドメイン設計だけでは埋まらない違和感 ドメイン設計を進めていると、以下のような状態に陥ることがありました。 ユースケースはある程度整理されている エンティティや値オブジェクトも定義できている しかし「本当にこれで使えるのか?」という違和感がある つまり、構造はあるが、実感がない状態です。 このとき不足していたのは、「ユーザーがどう操作するか」という視点でした。 ワイヤーフレームを作って起きた変化 試しにワイヤーフレームを作り始めたところ、明確な変化がありました。 1. ユーザーの行動が具体化される ワイヤーフレームでは「画面上で何をするか」を考える必要があります。 どこで入力するのか どの順番で操作するのか 何を確定とみなすのか これにより、ユースケースが単なる文章ではなく、操作として具体化されました。 2. 必要なデータと制約が見えてくる 操作を具体的にすると、自然と以下が見えてきます。 この画面では何のデータが必要か この操作はどんな条件で許されるのか ここで重要なのが、 不変条件(インバリアント) です。 例えば、口座振替管理アプリなら、 支払い元は必ず 1 つである 振替は「元 → 先」の関係を持つ 不正な組み合わせは作れない など、これらはドメイン設計だけでも定義できますが、 ワイヤーフレーム上で「操作」として考えることで、一気に現実味を帯びます。 3. ドメイン設計の「抜け」が露出する ワイヤーフレームを作る中で、次のような気づきが頻発しました。 この状態、どうやって作る? この操作、どのユースケースに対応する? このデータ、どこに属する? つまり、ドメイン設計で曖昧だった部分が強制的に表に出てくるのです。 ...

March 18, 2026 · 1 分 · 奥田 智紘

DDDで「集約はIDで参照する」とはどういう意味か

オブジェクト参照の場合 Order User アプリケーションサービス ID参照の場合 Order User アプリケーションサービス 違いを整理 オブジェクト参照 ID参照 もう一つ重要な違い:イベント駆動に拡張できる Application Service イベントの定義 EventBusの簡易実装 イベントハンドラ ハンドラ登録 イベントの流れ まとめ ドメイン駆動設計(DDD)では、次のようなルールをよく目にします。 集約は他の集約を IDで参照する しかし、初めてこのルールを見たときに疑問が浮かびます。 ID参照にしても結局 Repository.find(id) で取得できるのでは? それなら普通のオブジェクト参照とあまり変わらないのでは? この記事では、このルールの意味を 実際のコードで比較しながら説明します。 オブジェクト参照の場合 まず、他の集約を オブジェクトとして直接持つ設計を見てみます。 Order class Order( val id: OrderId, val user: User ) { fun place() { // 注文処理 user.upgradeToPremium() } } tips : place という単語には、 注文を出す / 発注する という意味があります。 User class User( val id: UserId, var plan: Plan ) { fun upgradeToPremium() { plan = Plan.PREMIUM } } アプリケーションサービス fun placeOrder(orderId: OrderId) { val order = orderRepository.find(orderId) order.place() orderRepository.save(order) } ここで起きていることを図にすると次のようになります。 ...

March 12, 2026 · 2 分 · 奥田 智紘

一つの集約にまとめるべきか、別の集約に分けるべきかの判断基準(DDD)

1. 不変条件を同時に守る必要があるか(最重要) 例 別集約になる例 2. ライフサイクルが一緒か 例 別集約の例 3. 集約は他の集約をIDで参照する 4. 同時に更新されるか 例 5. 集約は小さく保つ まとめ ドメイン駆動設計(DDD)で設計をしていると、次のような疑問に必ずぶつかります。 このエンティティは同じ集約に入れるべきか? それとも別の集約として切り離すべきか? 集約の境界は、DDDにおいて非常に重要な設計判断です。 この記事では、実務でよく使われる判断基準を整理します。 1. 不変条件を同時に守る必要があるか(最重要) 最も重要な基準は 不変条件(Invariant) です。 「このルールは常に同時に成立している必要があるか?」 YESなら 同じ集約 NOなら 別集約 例 注文と注文明細 Order └ OrderItem 不変条件 注文の合計金額 = 各注文金額の合計 注文明細の数量 ≥ 1 数量が変われば、注文全体の合計金額は変わります。 つまり、これらは 常に同時に成立している必要があります。 そのため Order と OrderItem は 同じ集約になります。 別集約になる例 Order Inventory (在庫) ルール 注文が確定したら在庫を減らす この処理は 注文確定 在庫更新 の2つの処理に分かれます。 これらは 同時トランザクションでなくても問題ないため Order集約 Inventory集約 と分けます。 2. ライフサイクルが一緒か 次に考えるのは ライフサイクルです。 「一緒に生まれて一緒に消えるか?」 YES → 同じ集約 NO → 別集約 例 Order └ OrderItem OrderItem は ...

March 12, 2026 · 1 分 · 奥田 智紘

ドメイン設計テンプレート

このドキュメントは、DDD(ドメイン駆動設計)に基づいて、ソフトウェアを設計するためのテンプレートです。 上から順番にテンプレートを埋めていくことで、 DDD に基づいたドメイン周りの設計がスムーズに行えるように作っています。 1. システム概要 システムの目的 対象ユーザー 解決したい課題 2. ユースケース ユースケース一覧 ユースケース詳細 【例】振替関係を作成する 入力 処理概要 出力 3. ユビキタス言語 概念 候補とその言葉のイメージ 賛否の意見 言葉の決定とその採用理由 ユビキタス言語辞書 4. エンティティ候補 5. 不変条件(Invariant) 6. 状態遷移 【例】TransferRelation 状態 状態遷移 7. 集約設計 集約一覧 集約詳細 【例】TransferRelation 属性 不変条件 ドメインメソッド 8. ドメインサービス サービス一覧 サービス詳細 【例】TransferService 9. リポジトリ 10. ユースケース設計 UseCase一覧 UseCase詳細 【例】CreateTransferRelationUseCase 処理 1. システム概要 システムの目的 このシステムは何を解決するものか。 【例】 フリーランスの固定費を管理する 口座振替の関係を可視化する 対象ユーザー このシステムのユーザー。 【例】 個人 フリーランス 企業 解決したい課題 【例】 固定費の支払い関係が分かりにくい 口座振替の全体構造を把握できない 2. ユースケース ユーザーがシステムで行う操作を列挙する。 ...

March 11, 2026 · 2 分 · 奥田 智紘

ドメイン設計テンプレート(補強版)

このドキュメントは、 ドメイン設計テンプレート に以下の項目を加えた補強版となっています。 4.境界コンテキスト 5.コンテキストマップ 7.値オブジェクト 12.ドメインイベント 15.集約境界図 16.不変条件の責任 1. システム概要 システムの目的 対象ユーザー 解決したい課題 2. ユースケース ユースケース一覧 ユースケース詳細 【例】振替関係を作成する 入力 処理概要 出力 3. ユビキタス言語 概念 候補とその言葉のイメージ 賛否の意見 言葉の決定とその採用理由 ユビキタス言語辞書 4. 境界コンテキスト 5. コンテキストマップ 6. エンティティ候補 7. 値オブジェクト 8. 不変条件(Invariant) 9. 状態遷移 【例】TransferRelation 状態 状態遷移 10. 集約設計 集約一覧 集約詳細 【例】TransferRelation 属性 不変条件 ドメインメソッド 11. ドメインサービス サービス一覧 サービス詳細 【例】TransferService 12. ドメインイベント イベント詳細 【例】TransferRelationCreated 発生タイミング ペイロード 13. リポジトリ 14. ユースケース設計 UseCase一覧 UseCase詳細 【例】CreateTransferRelationUseCase 処理 15. 集約境界図(Aggregate Boundary) 【例】TransferRelation 集約名 / Aggregate Root 内部エンティティ 値オブジェクト 集約境界 外部参照 集約ルール 16. 不変条件の責任(Invariant Responsibility) 不変条件一覧 不変条件の責任 振替日は1〜31 同じ口座 + サービスの振替関係は1つだけ 振替関係は必ず口座を持つ 振替関係は必ずサービスを持つ 不変条件の配置ルール 例 1. システム概要 システムの目的 このシステムは何を解決するものか。 ...

March 11, 2026 · 3 分 · 奥田 智紘

DDDにおける「集約」「ドメインサービス」「ユースケース」の違い

DDDにおける「集約」「ドメインサービス」「ユースケース」の違い まず結論 集約(Aggregate) ドメインサービス ユースケース(Application Service) ドメインロジックをどこに置くか 状態を持つドメインサービスはどうなるか ケース1:新しいドメイン概念が生まれた ケース2:一時的な処理状態 判断のための質問 よくある失敗 まとめ DDDにおける「集約」「ドメインサービス」「ユースケース」の違い DDD(ドメイン駆動設計)を学び始めると、多くの人が次の疑問にぶつかります。 集約とドメインサービスは何が違うのか ユースケースはどこまで責任を持つのか ドメインロジックはどこに書くべきなのか この記事では、この3つの概念を整理しながら、ドメインロジックをどこに置くべきかの判断基準を解説します。 まず結論 3つの役割は次のように整理できます。 集約(Aggregate) ドメインの状態を持つ 不変条件を守る ドメインモデルの中心 ドメインサービス(Domain Service) エンティティに属さないドメインロジック 複数の集約にまたがる処理 ユースケース(Use Case / Application Service) ユーザー操作を実現する手順 ドメインモデルを組み合わせる 依存関係は次のようになります。 UI ↓ UseCase ↓ DomainService ↓ Aggregate 重要なルールは 内側の層は外側を知らないこと です。 集約(Aggregate) 集約は 状態と不変条件を守るドメインモデル です。 例として「口座振替」を考えます。 振替関係には次のルールがあります。 振替日は 1〜31 「振替元 + 振替先」が同じ振替関係は 1 つだけ (重複不可) このルールを守る主体が TransferRelation です。 class TransferRelation( val accountId: AccountId, val serviceId: ServiceId, private var paymentDay: PaymentDay ) { fun changePaymentDay(newDay: PaymentDay) { paymentDay = newDay } fun stop() { // 状態変更 } } 【集約の特徴】 ...

March 10, 2026 · 2 分 · 奥田 智紘

DDDでドメインを設計するときの流れ

DDDでドメインを設計するときの流れ ドメイン設計の全体の流れ この記事の前提 1. ユースケースを書き出す 2. ドメイン用語を整理する(ユビキタス言語) 3. エンティティ候補を出す 4. 不変条件を書く 5. 状態遷移を整理する 6. 集約(Aggregate)を決める 7. エンティティの責務を設計する setterが問題になる理由 ドメインメソッドは「操作」を表す 重要な考え方 8. ドメインサービスを定義する 9. リポジトリを定義する 10. 最後にUIを作る まとめ この流れで設計する際のテンプレート DDDでドメインを設計するときの流れ ― 実践的な設計手順 ― ドメイン駆動設計(DDD)を学び始めると、多くの人が次の疑問を持ちます。 ドメイン設計は何から始めればいいのか 実際の開発ではどのような手順で設計するのか DDDの本では概念の説明が多く、「実際の設計手順」がはっきりしないことがあります。 この記事では、実務で使える ドメイン設計の具体的な手順 を整理して紹介します。 ドメイン設計の全体の流れ DDDでドメインを設計する場合、次の順序で進めると整理しやすくなります。 ユースケースを書き出す ドメイン用語を整理する(ユビキタス言語) エンティティ候補を出す 不変条件を書く 状態遷移を整理する 集約(Aggregate)を決める エンティティの責務を設計する 必要ならドメインサービスを作る リポジトリを定義する 最後にUIを設計する 重要なのは UIから設計を始めないこと です。 DDDでは、基本的には、 ユースケース → ドメイン → UI という順序で設計します。 実際には、一度でバシッと決められるわけではないため、ドメインを設計しているときに、ユースケースに戻るなど、各プロセス間を何度も行ったり来たりします。 この記事の前提 この記事では、口座振替管理アプリの開発を想定して、設計の流れを説明してきます。 振替元と振替先をユーザーが紐づけるメモアプリを想像してください。 1. ユースケースを書き出す 最初に、システムでユーザーが行う操作を書き出します。 ポイントは UIではなく行動を書くこと です。 ...

March 10, 2026 · 2 分 · 奥田 智紘

リファクタリングには二種類ある

リファクタリングには二種類ある ― 構造を変えるものと、概念を変えるもの まず整理:構造と概念の違い 構造の変更 概念の変更 なぜこの区別が重要なのか? なぜ通常は「構造 → 概念」なのか? ① 変更の安全性 ② デバッグのしやすさ ③ 複雑性の増幅 実務での使い分け(私の優先順位) 第1段階:可視化 第2段階:構造整理 第3段階:責務の明確化 第4段階:概念強化 まとめ リファクタリングには二種類ある ― 構造を変えるものと、概念を変えるもの 私は最近の失敗から、大きな学びを得ました。 それは、 リファクタリングには二種類ある ということです。 構造 を変えるリファクタリング 概念 を変えるリファクタリング この区別を意識していなかったことが、バグの原因でした。 この記事では、自分なりに整理した実務的な優先順位と共にまとめます。 まず整理:構造と概念の違い 構造の変更 関数抽出 共通化 重複削除 名前変更 ネスト解消 責務分離 振る舞いは変えない(意味は同じ) これは「コードの形」を整える作業です。 概念の変更 Boolean → sealed class nullable → 非null設計 エラー型明確化 状態遷移の型化 エンティティ再設計 ドメインモデル再定義 責務の再解釈 プログラムが表現する「意味」が変わる これは「コードが何を表現しているか」を変える作業です。 なぜこの区別が重要なのか? 私は「成功」という概念を Boolean で扱っていました。 しかし実際には: 作成成功 更新成功 失敗 という複数の意味がありました。 Boolean に押し込めたことで、 意味の差が見えなくなり、無意識に共通化してしまった のです。 ...

March 4, 2026 · 1 分 · 奥田 智紘

Boolean に潰された「状態」

Boolean に潰された「成功」 ― リファクタリングで気づいた抽象化の罠 はじめに 元のコード リファクタリングでやったこと 最初は「うっかり」だと思った 本当の原因 抽象化の粒度が粗すぎた 型で守る設計 学び 終わりに Boolean に潰された「成功」 ― リファクタリングで気づいた抽象化の罠 はじめに ViewModel の保存処理をリファクタリングしていたとき、 私は一度コードを壊しました。 原因は単純な「うっかりミス」に見えました。 しかし振り返ってみると、問題はもっと深いところにありました。 それは、 「成功」という概念を Boolean に潰してしまったこと でした。 この記事では、その過程と学びを書きます。 元のコード 保存処理は、新規作成と更新で分岐していました。 private fun saveData() { viewModelScope.launch { val isExistingItem: Boolean = (取得) if (destId == 0) { // 新規作成 val resultSuccess = directDebitDefRepo.createDestination(...) if (resultSuccess) { // フォームの初期化 _formInputState.update { FormInputState() } showSuccess() } else { showFailure() } } else { // 更新 val resultSuccess = directDebitDefRepo.updateDestination(...) if (resultSuccess) { showSuccess() } else { showFailure() } } } } ポイントはここです。 ...

March 4, 2026 · 2 分 · 奥田 智紘

Aggregate Root とは

Aggregate Root とは はじめに Aggregate(集約)とは Aggregate Root(集約ルート)とは 具体例(イメージ) 注文ドメインの例 なぜ Aggregate Root が必要なのか ① 整合性を守るため ② トランザクション境界を明確にするため ③ 依存関係をシンプルにするため 設計時の注意点 ① Aggregate を大きくしすぎない ② 他 Aggregate を直接参照しない ③ Repository は Aggregate Root 単位 よくある誤解 まとめ Aggregate Root とは はじめに DDD(Domain-Driven Design)の文脈で頻繁に登場する Aggregate Root(集約ルート) について解説していきます。 本記事では、 Aggregate / Aggregate Root とは何か なぜこの概念が必要なのか 設計時の注意点 を整理します。 Aggregate(集約)とは Aggregate とは、 関連する複数の Entity / Value Object を 一つのまとまり(整合性の境界) として扱う DDD の設計単位です。 重要なのは、 Aggregate は「常に一貫した状態を保つべき単位」 という点です。 ...

February 12, 2026 · 2 分 · 奥田 智紘

SQL の条件を固定しない

SQL の条件を固定しない はじめに 改善前の実装 DAO にドメインの意味が入り込んでいた 問題意識 改善方針 改善後の実装 DAO:検索軸だけを知る Repository / UseCase:意味を与える この設計で得られたメリット 1. SQL / DAO の数が増えにくくなった 2. DAO の責務が明確になった 3. ドメインの変更に強くなった おわりに SQL の条件を固定しない DAO の責務を整理して、ドメインに意味を寄せる設計改善 はじめに Android アプリ開発において、 「とりあえず動く」状態から一段上の設計に進もうとすると、 ViewModel・UseCase・Repository・DAO の責務の境界で悩むことが多い。 今回は、 SQL で条件を固定していた実装を見直し 条件をパラメータ化して SQL の数を減らし その「意味」を Domain / UseCase 側で与える という設計改善を行ったので、その考え方をまとめる。 改善前の実装 DAO にドメインの意味が入り込んでいた もともと DAO には、次のような Query が定義されていた。 @Query("SELECT * FROM transfer_item WHERE isSourceItem = 1") fun observeSources(): Flow<List<TransferItemEntity>> この実装はシンプルで分かりやすいが、次の問題を抱えていた。 DAO が「Source(振替元)」というドメインの意味を知っている 条件が増えるたびに SQL / DAO メソッドが増える 将来的に Query が爆発する兆候がある 問題意識 ここで違和感を覚えたのは、次の点だった。 ...

February 11, 2026 · 2 分 · 奥田 智紘

「サービスクラス」とは何か?

「サービスクラス」とは何か? 「サービス」という言葉の語源と基本ニュアンス Web / サーバーサイドでの Service クラスの起源 なぜ「サービスクラス」が問題視されるようになったのか DDD における「Service」との分岐 UseCase は何に対するアンチテーゼなのか Web エンジニアが「サービスクラス」と言うときの意味 1. 中立的な意味 2. 否定的な意味(設計議論) まとめ 「サービスクラス」とは何か? ──語源・ニュアンス・UseCaseとの違いを整理する 設計の議論をしていると、「それ、サービスクラスじゃない?」という言葉が出てくることがある。 一方で、Web エンジニアの中には「Service クラス」は普通に使う用語だ、という人も多い。 この「サービスクラス」という言葉は、厳密な定義がなく、文脈によって意味が変わるため、混乱を生みやすい。 この記事では、語源と歴史をたどりながら、 なぜ「サービス」と呼ばれるのか Web ではどのように使われてきたのか なぜ設計議論では否定的に使われることがあるのか UseCase と何が違うのか を整理する。 「サービス」という言葉の語源と基本ニュアンス 英語の service は、 奉仕 提供 役務 といった意味を持つ。 ソフトウェア設計においては、そこから転じて、 「他のオブジェクトのために処理を提供するもの」 という、かなり広い意味で使われるようになった。 この時点では、「サービス」はあくまで 役割を説明するための便利な言葉であり、設計上の厳密な概念ではない。 Web / サーバーサイドでの Service クラスの起源 「サービスクラス」という言葉が広く使われるようになったのは、 Java EE や Spring に代表される レイヤードアーキテクチャの文脈である。 典型的な構成は以下のようなものだ。 Controller ↓ Service ↓ Repository (DAO) ここでの Service クラスは、 Controller から呼ばれる 複数の Repository を組み合わせる トランザクション境界になる 業務ロジックを書く場所 という役割を担っていた。 ...

February 11, 2026 · 1 分 · 奥田 智紘

ViewModel が肥大化する理由

ViewModel が肥大化する理由 よくある誤解:ViewModel は「中継役」だから重くなる ViewModel が肥大化する本当の理由 「UI の判断」と「業務の判断」は別物 UseCase が登場する理由 ViewModel と UseCase の境界線 「ViewModel から処理がほとんど消えそう」問題 まとめ ViewModel が肥大化する理由 Android アプリを作っていると、ある日ふと気づきます。 ViewModel、でかくなりすぎじゃない? State の定義、Flow の合成、エラーハンドリング、変換ロジック、画面固有の分岐……。 気づけば 1 ファイルに数百行。しかも「どこを直すと何が壊れるのか分からない」状態。 この記事では、なぜ ViewModel は肥大化しやすいのか、そして それを防ぐための本質的な境界の引き方 について整理します。 よくある誤解:ViewModel は「中継役」だから重くなる よく言われる説明に、こんなものがあります。 ViewModel は UI と Domain の橋渡しだから Flow や State を扱うから 非同期処理が集まりやすいから どれも一理ありますが、本当の理由ではありません。 Flow があるから肥大化するのではなく、 ViewModel が 本来持つべきでない判断 を持ち始めたときに肥大化します。 ViewModel が肥大化する本当の理由 結論から言うと理由はシンプルです。 「何をするか」を ViewModel が決め始めるから 本来の ViewModel の役割は、次の 2 つです。 UI からのイベントを受け取る UI が描画しやすい State に変換して公開する ところが実際には、次のような責務が入り込みがちです。 ...

February 9, 2026 · 1 分 · 奥田 智紘

Domain と UseCase の違いを整理してみる

Domain と UseCase の違いを整理してみる 一言で言うと何が違うのか Domain とは何か Domain は「意味」と「ルール」の集合体 Domain の例 Domain がやらないこと UseCase とは何か UseCase は「動詞の層」 UseCase の例 Domain と UseCase の関係 なぜ UseCase が分かりにくいのか UseCase を作るべきタイミング Entity → Domain 変換はどこでやるべきか Domain と UseCase を分ける最大のポイント よくあるアンチパターン おわりに Domain と UseCase の違いを整理してみる Clean Architecture や DDD を学んでいると、ほぼ確実に次の疑問にぶつかります。 Domain と UseCase の違いがよく分からない どこまでが Domain で、どこからが UseCase なのか曖昧 UseCase を作ろうとすると、何を書けばいいのか分からない 私自身、このあたりで何度も立ち止まりました。 この記事では、実装経験を通して整理できた Domain と UseCase の違い を、自分なりの言葉でまとめてみます。 一言で言うと何が違うのか まず、かなり大胆に要約します。 Domain →「この世界では、何が正しく、何が成り立つか」を表す UseCase →「その正しさを、どういう手順・文脈で使うか」を表す ...

February 9, 2026 · 2 分 · 奥田 智紘

Domain レイヤーかどうかを判断する基準

Domain レイヤーかどうかを判断する基準 Domain レイヤーとは何か(簡単に) 判断基準:ユーザーがそれを意識するか? 例1:保存処理は Domain か? ケースA:保存先をユーザーが意識しない場合 ケースB:保存先をユーザーが意識的に選択する場合 「ユーザーが意識する」という基準の使いどころ UseCase との関係 注意点:すべてを Domain に入れない 迷ったときの最終チェック おわりに Domain レイヤーかどうかを判断する基準 ―「ユーザーが意識するか?」という視点― 設計をしていると、次のような悩みにぶつかることがあります。 この処理は Domain レイヤーに置くべきか? UseCase なのか、それとも Data レイヤーの責務なのか? そもそも Domain って何を置く場所なのか? とくに Clean Architecture や DDD を学び始めた頃は、 「正解の置き場所」を探そうとして、かえって混乱しがちです。 この記事では、私が設計を考える際に ひとつの判断基準 として使っている考え方を紹介します。 Domain レイヤーとは何か(簡単に) Domain レイヤーは、ざっくり言うと アプリが扱う 概念 その概念に関する ルール 「それは正しいかどうか」の判断 を表す層です。 技術的な詳細(DB、API、ライブラリなど)からは距離を置き、 アプリの意味そのもの を表現する場所だと考えています。 判断基準:ユーザーがそれを意識するか? 私が Domain レイヤーかどうかを判断する際に使っている基準は、次の問いです。 「ユーザーは、その機能や概念を意識してアプリを操作するか?」 この問いに YES なら、 その概念や処理は Domain レイヤー(または UseCase)に属する可能性が高いです。 NO なら、 それは Data レイヤーや Infrastructure に閉じ込めるべきものだと考えます。 ...

February 8, 2026 · 1 分 · 奥田 智紘

なぜクリーンアーキテクチャはドメインを守るのか

なぜクリーンアーキテクチャはドメインを守るのか はじめに 不変条件とは何か 例 不変条件は「判断」そのものである 不変条件が曖昧だと、設計は壊れる 不変条件をドメインに閉じ込める、という判断 なぜドメインなのか 「ドメインを守る」とは、何を守っているのか UI やインフラに不変条件を置くと何が起きるか まとめ おすすめの関連書籍(無料) なぜクリーンアーキテクチャはドメインを守るのか クリーンアーキテクチャはドメインを守っているが、その本質は「判断」を守ることである はじめに 「クリーンアーキテクチャでは、ドメインを守ることが重要だ」 この説明を、これまで何度も目にしてきましたし、自分でも何となく理解しているつもりでした。 UI から独立させるため フレームワークに依存させないため テストしやすくするため どれも間違ってはいません。 ただ、実務で設計に向き合えば向き合うほど、 それで結局、何が一番大事なのか? という疑問が残りました。 最近、その答えが少しはっきりしてきました。 クリーンアーキテクチャの本質は、 「判断をどこに閉じ込めるか?」を決める構造である という考え方です。 どの判断は UI に任せてよいのか どの判断はユースケースで行うべきか どの判断は、絶対に外に漏らしてはいけないのか この「判断の置き場所」を曖昧にしたまま設計すると、 責務が混ざり 修正のたびに迷いが生まれ 変更に弱い構造 になっていきます。 では、 絶対に外に出してはいけない判断とは何なのか? その答えを整理するための言葉が、不変条件 でした。 (不変条件という言葉は、英語では invariant という言葉でよく使用されます) この記事では、 クリーンアーキテクチャを「判断の置き場所」という視点で捉え直し その中で不変条件がどんな役割を持つのか なぜ不変条件をドメインに閉じ込めるのか を、実務の感覚に近い形で説明します。 不変条件とは何か 不変条件とは、 どれだけ仕様や実装が変わっても、絶対に破ってはいけない前提 です。 UI が変わっても API が変わっても DB が変わっても これが壊れたら、そのシステムは成立しない そういう条件を指します。 例 ログインしていないユーザーは、保護された操作をできない 残高は常に 0 以上である 同じ支払いは二重に確定しない これらは ...

January 15, 2026 · 1 分 · 奥田 智紘

クリーンアーキテクチャの本質をわかりやすく解説

アーキテクチャとは 依存関係とは 物理的な依存関係 意味的な依存関係 ドメインとは ドメイン以外 同心円状の図の意味 意味的な依存関係とクリーンアーキテクチャ 具体例 依存関係の逆転が必要なケース 依存関係を逆転させる具体的な方法 おすすめの記事 おすすめの本(無料) クリーンアーキテクチャという言葉はよく聞くけれど、「わかったようでわからない」という人が多いのではないでしょうか? クリーンアーキテクチャを解説した記事は、探せばすぐに出てきますが、あまり本質について語られている記事は少ないように感じます。 そこで、この記事でそれを簡潔に語っていこうと思います。 アーキテクチャとは まず、そもそも「アーキテクチャ」とは何でしょうか?簡潔に説明したいので、どんどん答えを書いていきます。 目的 壊れにくいソフトウェアを作る チーム内で統一した方針でソフトウェアを作る 実現方法 部品 (※1) 同士の依存関係 (※2) を整える 部品が果たす役割・責務を明確にする (※1) 「部品って具体的に何?」と思われた方は、一旦、クラスだと思っていただければ大丈夫です。 (※2) 後述します。 めちゃくちゃシンプルに書くとこうなると思います。 依存関係とは クリーンアーキテクチャの本質を知るためには、このセクションがめちゃくちゃ大事です!! 「そんなの知ってるわ」という方も、クリーンアーキテクチャについての理解が「モヤッ」としている方は読んでください。 依存関係には、二種類の依存関係があります。 物理的な依存関係 まずは、物理的な依存関係について説明します。これは、多くの人が最初に思い浮かぶ依存関係の方だと思われます。 すなわち、「どの部品 (クラスなど) がどの部品のことを知っているか」という関係性のことです。 (このドキュメントのサンプルコードは Kotlin で記述させていただきます。ご了承ください。) class A { val b: B } class B { // ... } 上記のサンプルでは、クラス A がクラス B を知っているので、これを「 A が B に依存している」と言います。 これが、物理的な依存関係です。 (超ざっくり) 意味的な依存関係 次に、意味的な依存関係について説明します。ここで、クリーンアーキテクチャの記事でよく出てくる同心円状の図を見て下さい。 (探せばすぐに出てくるのでここには載せません。笑) ...

January 14, 2026 · 2 分 · 奥田 智紘