Skip to content

feat!: Member集約を3状態モデルに再設計し、DiscordAccountを独立集約に分離#90

Closed
KinjiKawaguchi wants to merge 8 commits intodevelopfrom
worktree-member-aggregate-remodel
Closed

feat!: Member集約を3状態モデルに再設計し、DiscordAccountを独立集約に分離#90
KinjiKawaguchi wants to merge 8 commits intodevelopfrom
worktree-member-aggregate-remodel

Conversation

@KinjiKawaguchi
Copy link
Copy Markdown
Member

@KinjiKawaguchi KinjiKawaguchi commented Mar 20, 2026

Why

室員管理システムのドメインモデル設計書に基づき、ドメイン層を再設計する。
現行のMemberは単一クラスで全状態を扱い、Discord連携やDepartment(CS/BI/IA等)を集約内に持つ構造だが、仕様で求められる室員/未確認/元室員の状態遷移やAffiliationベースの所属管理に対応できていない。

What

Member集約の3状態モデル

  • ActiveMember(室員): MemberId, email, 名前, 個人メール, 学籍番号, 所属
  • UnconfirmedMember(未確認): MemberId, email, 名前, 個人メール
  • FormerMember(元室員): MemberId, email, 名前, 個人メール
  • type Member = ActiveMember | UnconfirmedMember | FormerMember

設計上の特徴

  • イミュータブル集約: 全操作が新インスタンスを返す
  • 型レベルでの状態保証: 元室員に学籍番号でアクセスしようとするとコンパイルエラー
  • 二重識別子: MemberId(UUID, Branded Type)は技術的識別子(DB PK, Event集約からの参照用)、emailはビジネス識別子
  • Affiliationによる区分表現: 学部生/修士/博士の区分はAffiliationの型が内包
  • ドメインイベント収集: 集約の操作でイベントが蓄積され、pullDomainEvents()で取り出し可能

ドメインイベント(12種)

室員登録、除籍、再登録、未確認移行、確認復帰、内部進学、転学部、転学科、転専攻、学籍番号変更、名前変更、個人メールアドレス変更

DiscordAccount集約(独立した境界づけられたコンテキスト)

  • 旧MemberからDiscordAccountを分離し、独自の集約ルートとして再設計
  • DiscordId(Branded Type)で型安全に識別
  • nickNameをDiscord固有の属性として保持
  • MemberIdでMember集約を参照

削除・整理

  • DiscordAccount(Member集約内のエンティティ)→ 独立集約に移行
  • Departments(CS/BI/IA等のenum)→ Affiliation(shared kernel)に置き換え
  • Discord関連例外、InvalidDepartmentException を削除

後続PRで対応するもの(Expand → Migrate → Contract)

  • Phase 1: スキーマExpand(status列・affiliation列の追加)
  • Phase 2: 手動データ移行(各メンバーのstatus/affiliationを設定)
  • Phase 3: スキーマContract(department列削除、student_id nullable化)
  • アプリケーション層(ユースケース書き換え・新規追加)

Test plan

  • ActiveMember: 登録、復元、除籍、未確認移行、名前変更、学籍番号変更
  • ActiveMember: 内部進学(学部→修士、修士→博士、不正遷移のエラー)
  • ActiveMember: 転学部、転学科(同一学部制約)、転専攻(同一研究科制約)
  • UnconfirmedMember: 確認復帰、除籍、名前変更
  • FormerMember: 再登録、名前変更
  • ライフサイクル全体のイベント蓄積(登録→除籍→再登録、登録→未確認→確認)
  • ドメイン層の型エラー: 0件
  • 全64テスト合格

🤖 Generated with Claude Code

@KinjiKawaguchi KinjiKawaguchi changed the title refactor!: Member集約をActiveMember/UnconfirmedMember/FormerMemberに再設計 feat!: Member集約をActiveMember/UnconfirmedMember/FormerMemberに再設計 Mar 20, 2026
@KinjiKawaguchi KinjiKawaguchi force-pushed the worktree-member-aggregate-remodel branch from 29393c4 to 988d3a8 Compare March 20, 2026 16:20
@KinjiKawaguchi KinjiKawaguchi changed the base branch from feat/karte-domain-model to develop March 20, 2026 16:21
@KinjiKawaguchi KinjiKawaguchi force-pushed the worktree-member-aggregate-remodel branch from 988d3a8 to af3afec Compare March 20, 2026 16:23
@KinjiKawaguchi KinjiKawaguchi changed the title feat!: Member集約をActiveMember/UnconfirmedMember/FormerMemberに再設計 feat!: Member集約を再設計 Mar 20, 2026
KinjiKawaguchi and others added 5 commits March 22, 2026 04:46
室員管理の仕様に基づきMember集約をリモデリング:
- 識別子をUUIDから大学メールアドレスに変更
- discriminated unionで室員/未確認/元室員の3状態を型レベルで表現
- 状態に応じた属性の有無を型システムで強制
- イミュータブルな集約(操作は新インスタンスを返す)
- ドメインイベントの定義と集約内での収集機構を追加
- DiscordAccountを集約から除外(外部サービス連携コンテキストへ移行予定)
- Departmentを削除しAffiliation(shared kernel)で所属を表現

BREAKING CHANGE: Member集約のAPIが全面的に変更

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ExternalServiceLink集約: 外部サービスとのアカウント紐付けを独立した境界づけられたコンテキストとして新設
- Faculty集約: 学部・学科のマスターデータ管理(学科の追加・削除、同名重複チェック)
- GraduateSchool集約: 研究科・専攻のマスターデータ管理(課程別管理、専攻の追加・削除)
- 不要な例外を削除(DiscordAccount系、InvalidDepartment)し、マスターデータ用例外を追加

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
組織構造はuniversityStructure.tsの型定義で管理されており、
ランタイムCRUD用のエンティティは不要。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- MemberSuspendedをMemberUnconfirmedにリネーム(クラス名・メソッド名との整合性)
- 未使用のExternalServiceUnlinkedイベントを削除(YAGNI)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
対応サービスを型レベルで制限し、不正な値の混入を防ぐ。
現時点ではDiscordのみ対応。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@KinjiKawaguchi KinjiKawaguchi force-pushed the worktree-member-aggregate-remodel branch from 986b7f9 to f615833 Compare March 21, 2026 19:47
KinjiKawaguchi and others added 2 commits March 22, 2026 05:06
UUIDをDBのPKおよびEvent集約からの参照用の技術的識別子として保持する。
emailはビジネス上の識別子として併存する。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
汎用的なExternalServiceLinkはSpeculative Generalityだったため削除し、
Discord固有のDiscordAccount集約として再設計:
- DiscordId(Branded Type)で型安全に識別
- nickNameをDiscord固有の属性として保持
- MemberIdでMember集約を参照

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@KinjiKawaguchi KinjiKawaguchi changed the title feat!: Member集約を再設計 feat!: Member集約を3状態モデルに再設計し、DiscordAccountを独立集約に分離 Mar 21, 2026
personalEmailは任意項目のため、null/undefinedではなくRecorded<Email>で
記録有無を明示する。Recorded型をKarte集約からshared kernelに移動。

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.

1 participant