Skip to content

feat: カルテのユースケース(作成・訂正・閲覧)を追加#84

Closed
KinjiKawaguchi wants to merge 209 commits into
developfrom
feat/karte-usecases
Closed

feat: カルテのユースケース(作成・訂正・閲覧)を追加#84
KinjiKawaguchi wants to merge 209 commits into
developfrom
feat/karte-usecases

Conversation

@KinjiKawaguchi

@KinjiKawaguchi KinjiKawaguchi commented Mar 12, 2026

Copy link
Copy Markdown
Member

概要

PR #82 で導入したカルテ集約のドメインモデルに対し、アプリケーション層のユースケースを追加する。

変更内容

新規ファイル

  • src/application/usecase/karte/CreateKarte.ts — カルテ新規作成
  • src/application/usecase/karte/CorrectKarte.ts — カルテの訂正
  • src/application/usecase/karte/GetKarte.ts — カルテ閲覧
  • src/application/usecase/karte/index.ts — barrel export

変更ファイル

  • src/application/index.ts — karteユースケースのexportを追加

設計の要点

ユースケースの粒度

  • Create: Karte.create() を呼び出し、全フィールドが揃った状態で新規作成する
  • Correct: 既存カルテを取得し karte.correct() で訂正した新しいインスタンスを保存する。「更新(Update)」ではなく「訂正(Correct)」という業務概念を採用
  • Get: KarteIdで1件取得

旧PR #84からの変更点

理由
UpdateKarteUseCase(部分更新) CorrectKarteUseCase(全フィールド訂正) Karteがイミュータブル設計になり correct() が全内容を受け取る
ListKartesUseCase + KarteFilter 削除 Repository が findById / save のみのYAGNI設計
Response SupportRecord PR #82 での命名変更に追従
new Karte(...) Karte.create(...) コンストラクタが private になりファクトリメソッド経由に

一覧取得について

現時点ではRepositoryに findAll を設けていない。一覧取得が必要になった段階で、クエリ専用のRead Modelやビュー層での対応を検討する(CQRSの方向性)。

テスト計画

  • npx tsc --noEmit で型チェックパス
  • npx biome check でリント・フォーマットパス

🤖 Generated with Claude Code

KinjiKawaguchi and others added 30 commits November 6, 2024 12:04
KinjiKawaguchi and others added 3 commits February 1, 2026 19:40
## Summary

- `domain/value-objects/` を廃止し、各値オブジェクトを使用元の集約ディレクトリに移動
  - `Email`, `UniversityEmail`, `Departments` → `aggregates/member/`
  - `LightningTalkDuration`, `Url` → `aggregates/event/`
- `ValueObject` 基底クラスを `domain/base/` に移動
- 型別ではなくドメイン概念別のディレクトリ構成に統一

## Motivation


既存の構造では値オブジェクトがビルディングブロックの型別(`value-objects/`)にトップレベルで切り出されており、どの集約に属する概念なのかが不明瞭だった。

各値オブジェクトは実際には特定の集約でのみ使用されているため、使用元の集約ディレクトリに配置することで、ドメイン概念の凝集度を高める。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 12, 2026 05:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

カルテ(相談記録)ドメインの新規集約と、作成・更新・閲覧・一覧取得のユースケースを追加するPRです。あわせて学籍番号や所属情報などの共有ドメイン型を導入しています。

Changes:

  • カルテ集約(Karte/Client/Consent/Consultation/Response等)と KarteRepository(フィルタ付き findAll)を追加
  • カルテの作成・更新(部分更新)・取得・一覧取得ユースケースを追加
  • 共有ドメインとして所属(Affiliation)と学籍番号(StudentId)を追加、例外型を拡充

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/domain/index.ts shared ドメイン公開を追加し、他層から参照可能に
src/domain/aggregates/index.ts karte 集約のエクスポートを追加
src/domain/aggregates/karte/index.ts karte 配下の型/クラス公開を追加
src/domain/aggregates/karte/Karte.ts カルテ集約ルートと更新/スナップショット生成を追加
src/domain/aggregates/karte/KarteRepository.ts KarteFilter と Repository I/F を追加
src/domain/aggregates/karte/Client.ts 相談者(学生/教職員)型を追加
src/domain/aggregates/karte/Consent.ts 同意事項の値型を追加
src/domain/aggregates/karte/Consultation.ts 相談事の値型を追加
src/domain/aggregates/karte/ConsultationCategory.ts 相談カテゴリVO(バリデーション付き)を追加
src/domain/aggregates/karte/Response.ts 対応事の値型を追加
src/domain/aggregates/karte/Resolution.ts 解決ステータス型を追加
src/domain/aggregates/karte/FollowUpDestination.ts 後処理先型を追加
src/domain/aggregates/karte/WorkDuration.ts 作業時間(記録あり/なし)の表現を追加
src/application/karte/index.ts karte ユースケース公開を追加
src/application/karte/CreateKarteUseCase.ts カルテ作成ユースケースを追加
src/application/karte/UpdateKarteUseCase.ts カルテ更新(部分更新)ユースケースを追加
src/application/karte/GetKarteUseCase.ts カルテ取得ユースケースを追加
src/application/karte/ListKartesUseCase.ts カルテ一覧(フィルタ)ユースケースを追加
src/domain/shared/index.ts shared エクスポートの追加
src/domain/shared/StudentId.ts 学籍番号VO(正規化+バリデーション)を追加
src/domain/shared/affiliation/index.ts affiliation エクスポートの追加
src/domain/shared/affiliation/Affiliation.ts 所属VO群(学部/修士/博士/専門職)を追加
src/domain/shared/affiliation/universityStructure.ts 静岡大学の所属構造を型レベルで定義
src/domain/exceptions/DomainExceptions.ts 所属・学籍番号・カテゴリ・作業時間などの例外型を追加

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/domain/aggregates/karte/Karte.ts Outdated
Comment thread src/domain/aggregates/karte/Karte.ts Outdated
Comment thread src/domain/shared/affiliation/Affiliation.ts
Comment thread src/domain/aggregates/karte/WorkDuration.ts Outdated
Comment thread src/domain/aggregates/karte/KarteRepository.ts Outdated
Comment thread src/application/karte/GetKarteUseCase.ts Outdated
KinjiKawaguchi and others added 3 commits March 12, 2026 15:09
## Summary
- vitest v4をdevDependenciesに追加し、`npm test` / `npm run test:watch`
スクリプトを追加
- `vitest.config.ts`でパスエイリアス(`#domain`, `#application`,
`#infrastructure`)を解決
- `ValueObject`基底クラスのユニットテストをサンプルとして追加(8テストケース)
- GitHub Actions `test.yml`ワークフローを追加(push/PR時に自動実行)

## Test plan
- [x] `npm test` で8件のテストが全てパスすること
- [x] `npx biome check` がパスすること
- [x] GitHub Actions test workflowが正常に実行されること

🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/su-its/core/pull/83"
target="_blank">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open with Devin">
  </picture>
</a>
<!-- devin-review-badge-end -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- 4つの個別CIワークフロー(build/lint/typecheck/test)を `ci.yml` に統合(Reusable
Workflow)
- `setup-node` Composite Action でセットアップ処理(setup-node + npm ci)を共通化
- `publish.yml` が `ci.yml` を前提条件として呼び出すオーケストレーションを追加
- 全ジョブに `permissions: contents: read` と `timeout-minutes: 10` を設定
- アクションを SHA pinning に更新(Node.js 20 deprecation 警告の解消)

## マージ後に必要な作業
- [ ] Branch Protection Rules の required status checks を `CI / lint`,
`CI / typecheck`, `CI / test`, `CI / build` に更新
- [ ] NPM_TOKEN シークレットの更新(Publish失敗の根本原因)
- [ ] GitHub Settings → Environments で `npm` 環境を作成
- [ ] 旧 `code_check.yml` ワークフローの無効化


🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/su-its/core/pull/88"
target="_blank">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open with Devin">
  </picture>
</a>
<!-- devin-review-badge-end -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- 学籍番号(StudentId)の値オブジェクトをSharedKernelとして追加
- 旧形式(8桁数字)と新形式(3桁数字+英字+4桁数字)の両方に対応
- `fromString` ファクトリメソッドで小文字→大文字正規化・空白トリムを実施
- private constructorにより直接インスタンス化を防止
- 12件のユニットテストを追加

## Test plan
- [x] 旧形式・新形式の正常系テスト
- [x] 大文字正規化・空白トリムのテスト
- [x] 桁数不正・英字のみ・英字位置不正・空文字等の異常系テスト
- [x] equalsメソッドの等価性テスト
- [x] lint, typecheck, test全てパス

## Related
- PR #81 から StudentId 部分を分割したPR
- マージ後、PR #81 をリベースしてAffiliation部分のみにする

🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/su-its/core/pull/87"
target="_blank">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open with Devin">
  </picture>
</a>
<!-- devin-review-badge-end -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@KinjiKawaguchi KinjiKawaguchi marked this pull request as draft March 12, 2026 07:37
KinjiKawaguchi and others added 2 commits March 14, 2026 21:47
## Summary
- `Event.removeExhibitMemberId`のボディが空で、展示からのメンバー削除がno-opだった問題を修正
- 該当Exhibitからメンバーを削除し、他のExhibitにも所属していなければEvent.memberIdsからも削除する
- PR #81 のレビューで指摘された pre-existing issue への対応

🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/su-its/core/pull/85"
target="_blank">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open with Devin">
  </picture>
</a>
<!-- devin-review-badge-end -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- 値オブジェクトを所属する集約ディレクトリに移動し、`domain/base` に `ValueObject` を配置
- Shared Kernel として `Affiliation`(学部・修士・博士・専門職)と `StudentId` 値オブジェクトを追加
- 静岡大学の全組織構造を型レベルの判別共用体で表現(学部ごとに異なる階層構造を正確にモデル化)
- Affiliation のバリデーションは型制約で保証し、ランタイム検証はシステム境界の責務とする設計


🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/su-its/core/pull/81"
target="_blank">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open with Devin">
  </picture>
</a>
<!-- devin-review-badge-end -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@KinjiKawaguchi KinjiKawaguchi changed the base branch from develop to feat/karte-domain-model March 15, 2026 00:20
@KinjiKawaguchi KinjiKawaguchi changed the title feat: カルテのユースケース(作成・更新・閲覧・一覧取得)を追加 feat: カルテのユースケース(作成・訂正・閲覧)を追加 Mar 15, 2026
@KinjiKawaguchi KinjiKawaguchi force-pushed the feat/karte-usecases branch 3 times, most recently from 9b29cef to 7fe507e Compare March 20, 2026 16:40
## 概要

PC相談室のカルテ(相談記録)を表すKarte集約を追加する。

## 背景

PC相談室では相談対応の記録をカルテとして管理している。このPRではカルテのドメインモデルを定義し、ドメイン層に集約として追加する。

## 集約の構造

```
Karte(集約ルート / immutable)
├── Client          相談者(Student | Teacher | Staff | Other)
├── Consent         同意事項
├── Consultation    相談事
│   ├── categories      相談カテゴリ(複数選択)
│   ├── targetDevice    対象機器
│   └── troubleDetails  トラブル詳細
└── SupportRecord   対応記録
    ├── assignedMemberIds  担当メンバー
    ├── content            対応内容
    ├── resolution         解決ステータス(resolved | unresolved → FollowUp)
    └── workDuration       作業時間
```

## 設計の要点

### 1. immutableな「記録型」集約

カルテは一度書いたら変わらない「記録」としてimmutableに設計した。
- `create()` で新規作成、`correct()` で訂正(記録ミスの修正用)
- `correct()` は `recordedAt` を保持し `lastUpdatedAt` のみ更新する
- `reconstruct()` は永続化データからの復元用(バリデーションなし)

### 2. Recorded\<T\> — 過去データの欠損を型で表現

過去のカルテには一部フィールドが存在しない。null/undefinedに暗黙の意味を持たせず、`{ type: "recorded",
value: T } | { type: "notRecorded" }` の判別共用体で明示する。

- `create()` では全フィールドを生の値で受け取り、内部で `recorded()` に包む → 新規カルテは常に完全
- `reconstruct()` では `Recorded<T>` をそのまま受け取る → 過去データの欠損を許容

### 3. Branded型 + ファクトリ関数によるVO実装

`WorkDuration`, `KarteId` などの値オブジェクトは、既存の `ValueObject<T>`
基底クラスを使わずbranded型 + ファクトリ関数で実装した。

理由: `ValueObject<T>` 基底クラスは `equals()` が JSON.stringify
比較に依存しており実装として脆い。今後他の集約でも基底クラスから剥がしていく可能性があるため、新規コードでは採用しなかった。

### 4. KarteRepositoryは find/save のみ

ユースケースが明確なメソッドだけ定義し、CRUD網羅はしない(YAGNI)。検索や一覧のユースケースが出た時点で追加する。

### 5. Recorded\<T\> の値にはドメインルールを適用

`Recorded<T>` が `recorded` であれば、その値はドメインルールに従うべき。
- `assignedMemberIds`: `Recorded<NonEmptyArray<string>>` — 記録されているなら1人以上
- `categories`: `Recorded<NonEmptyArray<ConsultationCategory>>` —
記録されているなら1つ以上

## 議論したいこと

- **カルテは本当に変更すべきでないか?**
現在の設計は「記録型(immutable)」を前提としている。correct()は記録ミスの訂正用として用意したが、そもそもカルテは一度記録したら変更不可とすべきか、それとも訂正を許容すべきか。
- **変更履歴を残すべきか?**
現在のcorrect()は最新の状態だけを保持し、変更前の内容は残らない。訂正を許容する場合、誰がいつ何を変更したかの履歴(監査ログ)が必要ではないか。
- **FollowUp「その他」選択時の自由記述フィールド** が必要か(ドメインエキスパートとの議論待ち)

## テスト

- `WorkDuration`: バリデーション境界値(0分OK、負数/小数/NaN → 例外)
- `Karte`:
create/correctのドメインロジック(タイムスタンプ、Recorded包装、訂正時のid/recordedAt保持)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Base automatically changed from feat/karte-domain-model to develop March 20, 2026 16:52
KinjiKawaguchi and others added 4 commits March 21, 2026 19:31
## Why

集約ルートのID(Member,
Event)および子エンティティのID(Exhibit)が`string`型であり、異なるID種別の取り違えがコンパイル時に検出できなかった。既存のKarteIdパターンに合わせてBranded
Typeを導入し、型安全性を確保する。

## What

- `MemberId`, `EventId`, `ExhibitId` のBranded Type定義とファクトリ関数を追加
- ドメイン層(集約ルート、エンティティ、リポジトリインターフェース、値オブジェクト)の全ID参照をBranded Typeに変更
- アプリケーション層(全ユースケースのInput型)をBranded Typeに変更
- インフラ層(DrizzleRepository)のDB↔ドメイン変換境界でファクトリ関数によるキャストを追加
- テストコードのassignedMemberIdsをMemberIdに更新

## BREAKING CHANGE

全集約ルートID(MemberId, EventId)およびExhibitIdの型が`string`からBranded Typeに変更。
呼び出し側は`memberId()`, `eventId()`, `exhibitId()`ファクトリ関数でIDを生成する必要がある。

🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/su-its/core/pull/92"
target="_blank">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open with Devin">
  </picture>
</a>
<!-- devin-review-badge-end -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR #82のドメインモデルに合わせてユースケースを再設計した。
- UpdateKarte → CorrectKarte: イミュータブルなKarte.correct()に対応
- ListKartesを削除: RepositoryがfindById/saveのみのYAGNI設計
- Response → SupportRecord: 型名変更に追従

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
プレゼン層でのエラー種別分岐を可能にするため、汎用Errorから
既存パターンに合わせたApplicationExceptionに変更する。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants