From b08350cd6744473282adaceabd7baac27337c46e Mon Sep 17 00:00:00 2001 From: KinjiKawaguchi Date: Thu, 12 Mar 2026 13:44:31 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20Shared=20Kernel=20=E3=81=AB=20Affil?= =?UTF-8?q?iation=20=E5=80=A4=E3=82=AA=E3=83=96=E3=82=B8=E3=82=A7=E3=82=AF?= =?UTF-8?q?=E3=83=88=E3=81=A8=20StudentId=20=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 静岡大学の全学部・研究科の組織構造を型レベルで表現し、 ValueObject パターンでランタイムバリデーション付きの Affiliation クラス群を実装。 - UndergraduateAffiliation / MasterAffiliation / DoctoralAffiliation / ProfessionalAffiliation - 学部ごとに異なる分類子(学科・課程・専攻・コース・専修)を discriminated union で表現 - StudentId 値オブジェクト(旧形式8桁 / 新形式3桁+英字+4桁) - 関連するドメイン例外を追加 Co-Authored-By: Claude Opus 4.6 --- src/domain/exceptions/DomainExceptions.ts | 21 ++ src/domain/shared/affiliation/Affiliation.ts | 43 +++ .../affiliation/affiliationValidator.ts | 288 ++++++++++++++++++ src/domain/shared/affiliation/index.ts | 2 + .../shared/affiliation/universityStructure.ts | 279 +++++++++++++++++ src/domain/shared/index.ts | 1 + 6 files changed, 634 insertions(+) create mode 100644 src/domain/shared/affiliation/Affiliation.ts create mode 100644 src/domain/shared/affiliation/affiliationValidator.ts create mode 100644 src/domain/shared/affiliation/index.ts create mode 100644 src/domain/shared/affiliation/universityStructure.ts diff --git a/src/domain/exceptions/DomainExceptions.ts b/src/domain/exceptions/DomainExceptions.ts index 0168334..d3da874 100644 --- a/src/domain/exceptions/DomainExceptions.ts +++ b/src/domain/exceptions/DomainExceptions.ts @@ -105,6 +105,27 @@ export class ExhibitHasMemberException extends DomainException { } } +export class InvalidAffiliationException extends DomainException { + constructor(detail: string) { + super(`無効な所属情報です: ${detail}`); + this.name = "InvalidAffiliationException"; + } +} + +export class InvalidDepartmentForFacultyException extends DomainException { + constructor(faculty: string, department: string) { + super(`学部「${faculty}」に学科「${department}」は存在しません`); + this.name = "InvalidDepartmentForFacultyException"; + } +} + +export class InvalidMajorForSchoolException extends DomainException { + constructor(school: string, major: string) { + super(`研究科「${school}」に専攻「${major}」は存在しません`); + this.name = "InvalidMajorForSchoolException"; + } +} + export class InvalidStudentIdException extends DomainException { constructor(studentId: string) { super( diff --git a/src/domain/shared/affiliation/Affiliation.ts b/src/domain/shared/affiliation/Affiliation.ts new file mode 100644 index 0000000..c216bbc --- /dev/null +++ b/src/domain/shared/affiliation/Affiliation.ts @@ -0,0 +1,43 @@ +import { ValueObject } from "#domain/base/ValueObject"; +import { + validateDoctoralValue, + validateMasterValue, + validateProfessionalValue, + validateUndergraduateValue, +} from "./affiliationValidator"; +import type { + DoctoralAffiliationValue, + MasterAffiliationValue, + ProfessionalAffiliationValue, + UndergraduateAffiliationValue, +} from "./universityStructure"; + +export class UndergraduateAffiliation extends ValueObject { + protected validate(): void { + validateUndergraduateValue(this.value); + } +} + +export class MasterAffiliation extends ValueObject { + protected validate(): void { + validateMasterValue(this.value); + } +} + +export class DoctoralAffiliation extends ValueObject { + protected validate(): void { + validateDoctoralValue(this.value); + } +} + +export class ProfessionalAffiliation extends ValueObject { + protected validate(): void { + validateProfessionalValue(this.value); + } +} + +export type Affiliation = + | UndergraduateAffiliation + | MasterAffiliation + | DoctoralAffiliation + | ProfessionalAffiliation; diff --git a/src/domain/shared/affiliation/affiliationValidator.ts b/src/domain/shared/affiliation/affiliationValidator.ts new file mode 100644 index 0000000..cc67c49 --- /dev/null +++ b/src/domain/shared/affiliation/affiliationValidator.ts @@ -0,0 +1,288 @@ +/** + * Affiliation値オブジェクトのランタイムバリデーション + * + * universityStructure.ts の型定義と対になるランタイム検証ロジック。 + * 外部入力(DB、API)から構築する際に、型レベルでは保証できない + * フィールド組み合わせの妥当性を検証する。 + */ +import { InvalidAffiliationException } from "#domain/exceptions"; + +// ── ヘルパー ── + +function fail(detail: string): never { + throw new InvalidAffiliationException(detail); +} + +function assertOneOf( + value: unknown, + valid: readonly string[], + label: string, +): asserts value is string { + if (typeof value !== "string" || !valid.includes(value)) { + fail(`無効な${label}です: ${value}`); + } +} + +function assertYearRange(year: unknown, max: number): void { + if ( + typeof year !== "number" || + !Number.isInteger(year) || + year < 1 || + year > max + ) { + fail(`無効な年次です: ${year} (1〜${max})`); + } +} + +// ── 学部バリデーションデータ ── + +const HUMANITIES_DEPTS: Readonly> = { + 昼間コース: ["社会学科", "言語文化学科", "法学科", "経済学科"], + 夜間主コース: ["法学科", "経済学科"], +}; + +const EDUCATION_SUBSPECIALTIES: Readonly< + Record +> = { + 発達教育学専攻: ["教育実践学専修", "教育心理学専修", "幼児教育専修"], + 初等学習開発学専攻: null, + 養護教育専攻: null, + 特別支援教育専攻: null, + 教科教育学専攻: [ + "国語教育専修", + "社会科教育専修", + "数学教育専修", + "理科教育専修", + "音楽教育専修", + "美術教育専修", + "保健体育教育専修", + "技術教育専修", + "家庭科教育専修", + "英語教育専修", + ], +}; + +const INFORMATICS_DEPTS = [ + "情報科学科", + "行動情報学科", + "情報社会学科", +] as const; + +const SCIENCE_DEPTS = [ + "数学科", + "物理学科", + "化学科", + "生物科学科", + "地球科学科", +] as const; + +const ENGINEERING_COURSES: Readonly> = + { + 機械工学科: [ + "宇宙・環境コース", + "知能・材料コース", + "電気機械システムコース", + ], + 電気電子工学科: [ + "情報エレクトロニクスコース", + "エネルギー・電子制御コース", + ], + 電子物質科学科: ["電子物理デバイスコース", "材料エネルギー化学コース"], + 化学バイオ工学科: ["環境応用化学コース", "バイオ応用工学コース"], + 数理システム工学科: null, + }; + +const AGRICULTURE_COURSES: Readonly> = + { + 生物資源科学科: ["バイオサイエンスコース", "環境サイエンスコース"], + 応用生命科学科: null, + }; + +const GLOBAL_COURSES = [ + "国際地域共生学コース", + "生命圏循環共生学コース", + "総合人間科学コース", +] as const; + +// ── 修士バリデーションデータ ── + +const HUMANITIES_MASTER_COURSES: Readonly> = { + 臨床人間科学専攻: ["臨床心理学コース", "臨床人間科学コース"], + 比較地域文化専攻: ["歴史・文化論コース", "言語文化論コース"], + 経済専攻: ["国際経営コース", "地域公共政策コース"], +}; + +const INTEGRATED_MASTER_COURSES: Readonly> = { + 情報学専攻: ["基盤情報学コース", "領域情報学コース"], + 理学専攻: [ + "数学コース", + "物理学コース", + "化学コース", + "生物科学コース", + "地球科学コース", + ], + 工学専攻: [ + "機械工学コース", + "電気電子工学コース", + "電子物質科学コース", + "化学バイオ工学コース", + "数理システム工学コース", + "事業開発マネジメントコース", + ], + 農学専攻: ["生物資源科学コース", "応用生命科学コース", "環境森林科学コース"], +}; + +// ── 博士バリデーションデータ ── + +const CREATIVE_DOCTORAL_MAJORS = [ + "ナノビジョン工学専攻", + "光・ナノ物質機能専攻", + "情報科学専攻", + "環境・エネルギーシステム専攻", + "バイオサイエンス専攻", +] as const; + +// ── バリデーション関数 ── + +export function validateUndergraduateValue(v: unknown): void { + const val = v as Record; + assertYearRange(val.year, 4); + + switch (val.faculty) { + case "人文社会科学部": { + assertOneOf( + val.enrollmentType, + Object.keys(HUMANITIES_DEPTS), + "課程区分", + ); + assertOneOf( + val.department, + HUMANITIES_DEPTS[val.enrollmentType as string], + "学科", + ); + break; + } + case "教育学部": { + if (val.program !== "学校教育教員養成課程") { + fail(`無効な課程です: ${val.program}`); + } + assertOneOf(val.major, Object.keys(EDUCATION_SUBSPECIALTIES), "専攻"); + const subs = EDUCATION_SUBSPECIALTIES[val.major as string]; + if (subs !== null) { + assertOneOf(val.subspecialty, subs, "専修"); + } + break; + } + case "情報学部": { + assertOneOf(val.department, [...INFORMATICS_DEPTS], "学科"); + break; + } + case "理学部": { + if ("department" in val && val.department !== undefined) { + assertOneOf(val.department, [...SCIENCE_DEPTS], "学科"); + } else if ("course" in val && val.course !== undefined) { + if (val.course !== "創造理学コース") { + fail(`無効なコースです: ${val.course}`); + } + } else { + fail("理学部には学科またはコースが必要です"); + } + break; + } + case "工学部": { + assertOneOf(val.department, Object.keys(ENGINEERING_COURSES), "学科"); + const courses = ENGINEERING_COURSES[val.department as string]; + if (courses !== null) { + assertOneOf(val.course, courses, "コース"); + } + break; + } + case "農学部": { + assertOneOf(val.department, Object.keys(AGRICULTURE_COURSES), "学科"); + const courses = AGRICULTURE_COURSES[val.department as string]; + if (courses !== null) { + assertOneOf(val.course, courses, "コース"); + } + break; + } + case "グローバル共創科学部": { + if (val.department !== "グローバル共創科学科") { + fail(`無効な学科です: ${val.department}`); + } + assertOneOf(val.course, [...GLOBAL_COURSES], "コース"); + break; + } + case "地域創造学環": + break; + default: + fail(`無効な学部です: ${val.faculty}`); + } +} + +export function validateMasterValue(v: unknown): void { + const val = v as Record; + assertYearRange(val.year, 2); + + switch (val.school) { + case "人文社会科学研究科": { + assertOneOf(val.major, Object.keys(HUMANITIES_MASTER_COURSES), "専攻"); + assertOneOf( + val.course, + HUMANITIES_MASTER_COURSES[val.major as string], + "コース", + ); + break; + } + case "総合科学技術研究科": { + assertOneOf(val.major, Object.keys(INTEGRATED_MASTER_COURSES), "専攻"); + assertOneOf( + val.course, + INTEGRATED_MASTER_COURSES[val.major as string], + "コース", + ); + break; + } + case "山岳流域研究院": + break; + default: + fail(`無効な研究科です: ${val.school}`); + } +} + +export function validateDoctoralValue(v: unknown): void { + const val = v as Record; + assertYearRange(val.year, 3); + + switch (val.school) { + case "創造科学技術大学院": { + assertOneOf(val.major, [...CREATIVE_DOCTORAL_MAJORS], "専攻"); + break; + } + case "教育学研究科": { + if (val.major !== "共同教科開発学専攻") { + fail(`無効な専攻です: ${val.major}`); + } + break; + } + case "光医工学研究科": { + if (val.major !== "光医工学共同専攻") { + fail(`無効な専攻です: ${val.major}`); + } + break; + } + default: + fail(`無効な研究科です: ${val.school}`); + } +} + +export function validateProfessionalValue(v: unknown): void { + const val = v as Record; + assertYearRange(val.year, 2); + + if (val.school !== "教育学研究科") { + fail(`無効な研究科です: ${val.school}`); + } + if (val.major !== "教育実践高度化専攻") { + fail(`無効な専攻です: ${val.major}`); + } +} diff --git a/src/domain/shared/affiliation/index.ts b/src/domain/shared/affiliation/index.ts new file mode 100644 index 0000000..54bad69 --- /dev/null +++ b/src/domain/shared/affiliation/index.ts @@ -0,0 +1,2 @@ +export * from "./Affiliation"; +export * from "./universityStructure"; diff --git a/src/domain/shared/affiliation/universityStructure.ts b/src/domain/shared/affiliation/universityStructure.ts new file mode 100644 index 0000000..8ee5fd5 --- /dev/null +++ b/src/domain/shared/affiliation/universityStructure.ts @@ -0,0 +1,279 @@ +/** + * 静岡大学の組織構造定義 + * + * 各学部・研究科の階層を型レベルで表現する。 + * 分類子は実際の名称に対応: Faculty(学部), Department(学科), Program(課程), + * Major(専攻), Course(コース), Subspecialty(専修) + */ + +// ── 年次 ── + +export type UndergraduateYear = 1 | 2 | 3 | 4; +export type MasterYear = 1 | 2; +export type DoctoralYear = 1 | 2 | 3; +export type ProfessionalYear = 1 | 2; + +// ── 学部(Undergraduate) ── + +export type HumanitiesFacultyValue = + | { + faculty: "人文社会科学部"; + enrollmentType: "昼間コース"; + department: "社会学科" | "言語文化学科" | "法学科" | "経済学科"; + year: UndergraduateYear; + } + | { + faculty: "人文社会科学部"; + enrollmentType: "夜間主コース"; + department: "法学科" | "経済学科"; + year: UndergraduateYear; + }; + +export type EducationFacultyValue = + | { + faculty: "教育学部"; + program: "学校教育教員養成課程"; + major: "発達教育学専攻"; + subspecialty: "教育実践学専修" | "教育心理学専修" | "幼児教育専修"; + year: UndergraduateYear; + } + | { + faculty: "教育学部"; + program: "学校教育教員養成課程"; + major: "初等学習開発学専攻"; + year: UndergraduateYear; + } + | { + faculty: "教育学部"; + program: "学校教育教員養成課程"; + major: "養護教育専攻"; + year: UndergraduateYear; + } + | { + faculty: "教育学部"; + program: "学校教育教員養成課程"; + major: "特別支援教育専攻"; + year: UndergraduateYear; + } + | { + faculty: "教育学部"; + program: "学校教育教員養成課程"; + major: "教科教育学専攻"; + subspecialty: + | "国語教育専修" + | "社会科教育専修" + | "数学教育専修" + | "理科教育専修" + | "音楽教育専修" + | "美術教育専修" + | "保健体育教育専修" + | "技術教育専修" + | "家庭科教育専修" + | "英語教育専修"; + year: UndergraduateYear; + }; + +export type InformaticsFacultyValue = { + faculty: "情報学部"; + department: "情報科学科" | "行動情報学科" | "情報社会学科"; + year: UndergraduateYear; +}; + +export type ScienceFacultyValue = + | { + faculty: "理学部"; + department: + | "数学科" + | "物理学科" + | "化学科" + | "生物科学科" + | "地球科学科"; + year: UndergraduateYear; + } + | { + faculty: "理学部"; + course: "創造理学コース"; + year: UndergraduateYear; + }; + +export type EngineeringFacultyValue = + | { + faculty: "工学部"; + department: "機械工学科"; + course: + | "宇宙・環境コース" + | "知能・材料コース" + | "電気機械システムコース"; + year: UndergraduateYear; + } + | { + faculty: "工学部"; + department: "電気電子工学科"; + course: "情報エレクトロニクスコース" | "エネルギー・電子制御コース"; + year: UndergraduateYear; + } + | { + faculty: "工学部"; + department: "電子物質科学科"; + course: "電子物理デバイスコース" | "材料エネルギー化学コース"; + year: UndergraduateYear; + } + | { + faculty: "工学部"; + department: "化学バイオ工学科"; + course: "環境応用化学コース" | "バイオ応用工学コース"; + year: UndergraduateYear; + } + | { + faculty: "工学部"; + department: "数理システム工学科"; + year: UndergraduateYear; + }; + +export type AgricultureFacultyValue = + | { + faculty: "農学部"; + department: "生物資源科学科"; + course: "バイオサイエンスコース" | "環境サイエンスコース"; + year: UndergraduateYear; + } + | { + faculty: "農学部"; + department: "応用生命科学科"; + year: UndergraduateYear; + }; + +export type GlobalCoCreationFacultyValue = { + faculty: "グローバル共創科学部"; + department: "グローバル共創科学科"; + course: + | "国際地域共生学コース" + | "生命圏循環共生学コース" + | "総合人間科学コース"; + year: UndergraduateYear; +}; + +export type RegionalDevelopmentValue = { + faculty: "地域創造学環"; + year: UndergraduateYear; +}; + +export type UndergraduateAffiliationValue = + | HumanitiesFacultyValue + | EducationFacultyValue + | InformaticsFacultyValue + | ScienceFacultyValue + | EngineeringFacultyValue + | AgricultureFacultyValue + | GlobalCoCreationFacultyValue + | RegionalDevelopmentValue; + +// ── 修士課程(Master) ── + +export type HumanitiesMasterValue = + | { + school: "人文社会科学研究科"; + major: "臨床人間科学専攻"; + course: "臨床心理学コース" | "臨床人間科学コース"; + year: MasterYear; + } + | { + school: "人文社会科学研究科"; + major: "比較地域文化専攻"; + course: "歴史・文化論コース" | "言語文化論コース"; + year: MasterYear; + } + | { + school: "人文社会科学研究科"; + major: "経済専攻"; + course: "国際経営コース" | "地域公共政策コース"; + year: MasterYear; + }; + +export type IntegratedSciTechMasterValue = + | { + school: "総合科学技術研究科"; + major: "情報学専攻"; + course: "基盤情報学コース" | "領域情報学コース"; + year: MasterYear; + } + | { + school: "総合科学技術研究科"; + major: "理学専攻"; + course: + | "数学コース" + | "物理学コース" + | "化学コース" + | "生物科学コース" + | "地球科学コース"; + year: MasterYear; + } + | { + school: "総合科学技術研究科"; + major: "工学専攻"; + course: + | "機械工学コース" + | "電気電子工学コース" + | "電子物質科学コース" + | "化学バイオ工学コース" + | "数理システム工学コース" + | "事業開発マネジメントコース"; + year: MasterYear; + } + | { + school: "総合科学技術研究科"; + major: "農学専攻"; + course: + | "生物資源科学コース" + | "応用生命科学コース" + | "環境森林科学コース"; + year: MasterYear; + }; + +export type MountainWatershedValue = { + school: "山岳流域研究院"; + year: MasterYear; +}; + +export type MasterAffiliationValue = + | HumanitiesMasterValue + | IntegratedSciTechMasterValue + | MountainWatershedValue; + +// ── 博士課程(Doctoral) ── + +export type CreativeSciTechDoctoralValue = { + school: "創造科学技術大学院"; + major: + | "ナノビジョン工学専攻" + | "光・ナノ物質機能専攻" + | "情報科学専攻" + | "環境・エネルギーシステム専攻" + | "バイオサイエンス専攻"; + year: DoctoralYear; +}; + +export type EducationDoctoralValue = { + school: "教育学研究科"; + major: "共同教科開発学専攻"; + year: DoctoralYear; +}; + +export type OptoBiomedicalDoctoralValue = { + school: "光医工学研究科"; + major: "光医工学共同専攻"; + year: DoctoralYear; +}; + +export type DoctoralAffiliationValue = + | CreativeSciTechDoctoralValue + | EducationDoctoralValue + | OptoBiomedicalDoctoralValue; + +// ── 専門職学位課程(Professional) ── + +export type ProfessionalAffiliationValue = { + school: "教育学研究科"; + major: "教育実践高度化専攻"; + year: ProfessionalYear; +}; diff --git a/src/domain/shared/index.ts b/src/domain/shared/index.ts index e3bd313..39ee67f 100644 --- a/src/domain/shared/index.ts +++ b/src/domain/shared/index.ts @@ -1 +1,2 @@ +export * from "./affiliation"; export * from "./StudentId"; From ac7bc9ac07927685e3567d8e95af75b471345271 Mon Sep 17 00:00:00 2001 From: KinjiKawaguchi Date: Thu, 12 Mar 2026 13:48:55 +0900 Subject: [PATCH 2/7] =?UTF-8?q?refactor:=20Affiliation=E3=81=AE=E3=83=A9?= =?UTF-8?q?=E3=83=B3=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=90=E3=83=AA=E3=83=87?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 型レベルの判別共用体で制約が保証されるため、 ランタイムバリデーションはValueObjectの責務ではなくシステム境界の責務とする。 Co-Authored-By: Claude Opus 4.6 --- src/domain/shared/affiliation/Affiliation.ts | 22 +- .../affiliation/affiliationValidator.ts | 288 ------------------ 2 files changed, 4 insertions(+), 306 deletions(-) delete mode 100644 src/domain/shared/affiliation/affiliationValidator.ts diff --git a/src/domain/shared/affiliation/Affiliation.ts b/src/domain/shared/affiliation/Affiliation.ts index c216bbc..5b04478 100644 --- a/src/domain/shared/affiliation/Affiliation.ts +++ b/src/domain/shared/affiliation/Affiliation.ts @@ -1,10 +1,4 @@ import { ValueObject } from "#domain/base/ValueObject"; -import { - validateDoctoralValue, - validateMasterValue, - validateProfessionalValue, - validateUndergraduateValue, -} from "./affiliationValidator"; import type { DoctoralAffiliationValue, MasterAffiliationValue, @@ -13,27 +7,19 @@ import type { } from "./universityStructure"; export class UndergraduateAffiliation extends ValueObject { - protected validate(): void { - validateUndergraduateValue(this.value); - } + protected validate(): void {} } export class MasterAffiliation extends ValueObject { - protected validate(): void { - validateMasterValue(this.value); - } + protected validate(): void {} } export class DoctoralAffiliation extends ValueObject { - protected validate(): void { - validateDoctoralValue(this.value); - } + protected validate(): void {} } export class ProfessionalAffiliation extends ValueObject { - protected validate(): void { - validateProfessionalValue(this.value); - } + protected validate(): void {} } export type Affiliation = diff --git a/src/domain/shared/affiliation/affiliationValidator.ts b/src/domain/shared/affiliation/affiliationValidator.ts deleted file mode 100644 index cc67c49..0000000 --- a/src/domain/shared/affiliation/affiliationValidator.ts +++ /dev/null @@ -1,288 +0,0 @@ -/** - * Affiliation値オブジェクトのランタイムバリデーション - * - * universityStructure.ts の型定義と対になるランタイム検証ロジック。 - * 外部入力(DB、API)から構築する際に、型レベルでは保証できない - * フィールド組み合わせの妥当性を検証する。 - */ -import { InvalidAffiliationException } from "#domain/exceptions"; - -// ── ヘルパー ── - -function fail(detail: string): never { - throw new InvalidAffiliationException(detail); -} - -function assertOneOf( - value: unknown, - valid: readonly string[], - label: string, -): asserts value is string { - if (typeof value !== "string" || !valid.includes(value)) { - fail(`無効な${label}です: ${value}`); - } -} - -function assertYearRange(year: unknown, max: number): void { - if ( - typeof year !== "number" || - !Number.isInteger(year) || - year < 1 || - year > max - ) { - fail(`無効な年次です: ${year} (1〜${max})`); - } -} - -// ── 学部バリデーションデータ ── - -const HUMANITIES_DEPTS: Readonly> = { - 昼間コース: ["社会学科", "言語文化学科", "法学科", "経済学科"], - 夜間主コース: ["法学科", "経済学科"], -}; - -const EDUCATION_SUBSPECIALTIES: Readonly< - Record -> = { - 発達教育学専攻: ["教育実践学専修", "教育心理学専修", "幼児教育専修"], - 初等学習開発学専攻: null, - 養護教育専攻: null, - 特別支援教育専攻: null, - 教科教育学専攻: [ - "国語教育専修", - "社会科教育専修", - "数学教育専修", - "理科教育専修", - "音楽教育専修", - "美術教育専修", - "保健体育教育専修", - "技術教育専修", - "家庭科教育専修", - "英語教育専修", - ], -}; - -const INFORMATICS_DEPTS = [ - "情報科学科", - "行動情報学科", - "情報社会学科", -] as const; - -const SCIENCE_DEPTS = [ - "数学科", - "物理学科", - "化学科", - "生物科学科", - "地球科学科", -] as const; - -const ENGINEERING_COURSES: Readonly> = - { - 機械工学科: [ - "宇宙・環境コース", - "知能・材料コース", - "電気機械システムコース", - ], - 電気電子工学科: [ - "情報エレクトロニクスコース", - "エネルギー・電子制御コース", - ], - 電子物質科学科: ["電子物理デバイスコース", "材料エネルギー化学コース"], - 化学バイオ工学科: ["環境応用化学コース", "バイオ応用工学コース"], - 数理システム工学科: null, - }; - -const AGRICULTURE_COURSES: Readonly> = - { - 生物資源科学科: ["バイオサイエンスコース", "環境サイエンスコース"], - 応用生命科学科: null, - }; - -const GLOBAL_COURSES = [ - "国際地域共生学コース", - "生命圏循環共生学コース", - "総合人間科学コース", -] as const; - -// ── 修士バリデーションデータ ── - -const HUMANITIES_MASTER_COURSES: Readonly> = { - 臨床人間科学専攻: ["臨床心理学コース", "臨床人間科学コース"], - 比較地域文化専攻: ["歴史・文化論コース", "言語文化論コース"], - 経済専攻: ["国際経営コース", "地域公共政策コース"], -}; - -const INTEGRATED_MASTER_COURSES: Readonly> = { - 情報学専攻: ["基盤情報学コース", "領域情報学コース"], - 理学専攻: [ - "数学コース", - "物理学コース", - "化学コース", - "生物科学コース", - "地球科学コース", - ], - 工学専攻: [ - "機械工学コース", - "電気電子工学コース", - "電子物質科学コース", - "化学バイオ工学コース", - "数理システム工学コース", - "事業開発マネジメントコース", - ], - 農学専攻: ["生物資源科学コース", "応用生命科学コース", "環境森林科学コース"], -}; - -// ── 博士バリデーションデータ ── - -const CREATIVE_DOCTORAL_MAJORS = [ - "ナノビジョン工学専攻", - "光・ナノ物質機能専攻", - "情報科学専攻", - "環境・エネルギーシステム専攻", - "バイオサイエンス専攻", -] as const; - -// ── バリデーション関数 ── - -export function validateUndergraduateValue(v: unknown): void { - const val = v as Record; - assertYearRange(val.year, 4); - - switch (val.faculty) { - case "人文社会科学部": { - assertOneOf( - val.enrollmentType, - Object.keys(HUMANITIES_DEPTS), - "課程区分", - ); - assertOneOf( - val.department, - HUMANITIES_DEPTS[val.enrollmentType as string], - "学科", - ); - break; - } - case "教育学部": { - if (val.program !== "学校教育教員養成課程") { - fail(`無効な課程です: ${val.program}`); - } - assertOneOf(val.major, Object.keys(EDUCATION_SUBSPECIALTIES), "専攻"); - const subs = EDUCATION_SUBSPECIALTIES[val.major as string]; - if (subs !== null) { - assertOneOf(val.subspecialty, subs, "専修"); - } - break; - } - case "情報学部": { - assertOneOf(val.department, [...INFORMATICS_DEPTS], "学科"); - break; - } - case "理学部": { - if ("department" in val && val.department !== undefined) { - assertOneOf(val.department, [...SCIENCE_DEPTS], "学科"); - } else if ("course" in val && val.course !== undefined) { - if (val.course !== "創造理学コース") { - fail(`無効なコースです: ${val.course}`); - } - } else { - fail("理学部には学科またはコースが必要です"); - } - break; - } - case "工学部": { - assertOneOf(val.department, Object.keys(ENGINEERING_COURSES), "学科"); - const courses = ENGINEERING_COURSES[val.department as string]; - if (courses !== null) { - assertOneOf(val.course, courses, "コース"); - } - break; - } - case "農学部": { - assertOneOf(val.department, Object.keys(AGRICULTURE_COURSES), "学科"); - const courses = AGRICULTURE_COURSES[val.department as string]; - if (courses !== null) { - assertOneOf(val.course, courses, "コース"); - } - break; - } - case "グローバル共創科学部": { - if (val.department !== "グローバル共創科学科") { - fail(`無効な学科です: ${val.department}`); - } - assertOneOf(val.course, [...GLOBAL_COURSES], "コース"); - break; - } - case "地域創造学環": - break; - default: - fail(`無効な学部です: ${val.faculty}`); - } -} - -export function validateMasterValue(v: unknown): void { - const val = v as Record; - assertYearRange(val.year, 2); - - switch (val.school) { - case "人文社会科学研究科": { - assertOneOf(val.major, Object.keys(HUMANITIES_MASTER_COURSES), "専攻"); - assertOneOf( - val.course, - HUMANITIES_MASTER_COURSES[val.major as string], - "コース", - ); - break; - } - case "総合科学技術研究科": { - assertOneOf(val.major, Object.keys(INTEGRATED_MASTER_COURSES), "専攻"); - assertOneOf( - val.course, - INTEGRATED_MASTER_COURSES[val.major as string], - "コース", - ); - break; - } - case "山岳流域研究院": - break; - default: - fail(`無効な研究科です: ${val.school}`); - } -} - -export function validateDoctoralValue(v: unknown): void { - const val = v as Record; - assertYearRange(val.year, 3); - - switch (val.school) { - case "創造科学技術大学院": { - assertOneOf(val.major, [...CREATIVE_DOCTORAL_MAJORS], "専攻"); - break; - } - case "教育学研究科": { - if (val.major !== "共同教科開発学専攻") { - fail(`無効な専攻です: ${val.major}`); - } - break; - } - case "光医工学研究科": { - if (val.major !== "光医工学共同専攻") { - fail(`無効な専攻です: ${val.major}`); - } - break; - } - default: - fail(`無効な研究科です: ${val.school}`); - } -} - -export function validateProfessionalValue(v: unknown): void { - const val = v as Record; - assertYearRange(val.year, 2); - - if (val.school !== "教育学研究科") { - fail(`無効な研究科です: ${val.school}`); - } - if (val.major !== "教育実践高度化専攻") { - fail(`無効な専攻です: ${val.major}`); - } -} From 0bb3aba82d7e4c956dfca5faed1ebed5ba75d2f7 Mon Sep 17 00:00:00 2001 From: KinjiKawaguchi Date: Thu, 12 Mar 2026 14:08:22 +0900 Subject: [PATCH 3/7] =?UTF-8?q?docs:=20=E5=9E=8B=E5=AE=9A=E7=BE=A9?= =?UTF-8?q?=E3=81=ABJSDoc=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E3=81=A8?= =?UTF-8?q?=E5=85=AC=E5=BC=8F=E3=83=9A=E3=83=BC=E3=82=B8URL=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 各学部・研究科の型定義に日本語名のJSDocコメントと 公式組織構成ページへの@seeリンクを追加。 バリデーター削除で不要になった例外クラスも削除。 Co-Authored-By: Claude Opus 4.6 --- src/domain/exceptions/DomainExceptions.ts | 14 ------ src/domain/shared/affiliation/Affiliation.ts | 12 +++-- .../shared/affiliation/universityStructure.ts | 46 +++++++++++++------ 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/domain/exceptions/DomainExceptions.ts b/src/domain/exceptions/DomainExceptions.ts index d3da874..dc68586 100644 --- a/src/domain/exceptions/DomainExceptions.ts +++ b/src/domain/exceptions/DomainExceptions.ts @@ -112,20 +112,6 @@ export class InvalidAffiliationException extends DomainException { } } -export class InvalidDepartmentForFacultyException extends DomainException { - constructor(faculty: string, department: string) { - super(`学部「${faculty}」に学科「${department}」は存在しません`); - this.name = "InvalidDepartmentForFacultyException"; - } -} - -export class InvalidMajorForSchoolException extends DomainException { - constructor(school: string, major: string) { - super(`研究科「${school}」に専攻「${major}」は存在しません`); - this.name = "InvalidMajorForSchoolException"; - } -} - export class InvalidStudentIdException extends DomainException { constructor(studentId: string) { super( diff --git a/src/domain/shared/affiliation/Affiliation.ts b/src/domain/shared/affiliation/Affiliation.ts index 5b04478..846832d 100644 --- a/src/domain/shared/affiliation/Affiliation.ts +++ b/src/domain/shared/affiliation/Affiliation.ts @@ -6,24 +6,28 @@ import type { UndergraduateAffiliationValue, } from "./universityStructure"; +/** 学部所属 */ export class UndergraduateAffiliation extends ValueObject { protected validate(): void {} } +/** 修士課程所属 */ export class MasterAffiliation extends ValueObject { protected validate(): void {} } +/** 博士課程所属 */ export class DoctoralAffiliation extends ValueObject { protected validate(): void {} } +/** 専門職学位課程所属 */ export class ProfessionalAffiliation extends ValueObject { protected validate(): void {} } export type Affiliation = - | UndergraduateAffiliation - | MasterAffiliation - | DoctoralAffiliation - | ProfessionalAffiliation; + | UndergraduateAffiliation // 学部 + | MasterAffiliation // 修士 + | DoctoralAffiliation // 博士 + | ProfessionalAffiliation; // 専門職 diff --git a/src/domain/shared/affiliation/universityStructure.ts b/src/domain/shared/affiliation/universityStructure.ts index 8ee5fd5..e9401af 100644 --- a/src/domain/shared/affiliation/universityStructure.ts +++ b/src/domain/shared/affiliation/universityStructure.ts @@ -4,6 +4,9 @@ * 各学部・研究科の階層を型レベルで表現する。 * 分類子は実際の名称に対応: Faculty(学部), Department(学科), Program(課程), * Major(専攻), Course(コース), Subspecialty(専修) + * + * @see https://www.shizuoka.ac.jp/subject/ + * @see https://www.ed.shizuoka.ac.jp/applicants/about/organization/ */ // ── 年次 ── @@ -15,6 +18,7 @@ export type ProfessionalYear = 1 | 2; // ── 学部(Undergraduate) ── +/** 人文社会科学部 @see https://www.hss.shizuoka.ac.jp/ */ export type HumanitiesFacultyValue = | { faculty: "人文社会科学部"; @@ -29,6 +33,7 @@ export type HumanitiesFacultyValue = year: UndergraduateYear; }; +/** 教育学部 @see https://www.ed.shizuoka.ac.jp/applicants/about/organization/ */ export type EducationFacultyValue = | { faculty: "教育学部"; @@ -73,12 +78,14 @@ export type EducationFacultyValue = year: UndergraduateYear; }; +/** 情報学部 @see https://www.inf.shizuoka.ac.jp/ */ export type InformaticsFacultyValue = { faculty: "情報学部"; department: "情報科学科" | "行動情報学科" | "情報社会学科"; year: UndergraduateYear; }; +/** 理学部 @see https://www.sci.shizuoka.ac.jp/dep_study */ export type ScienceFacultyValue = | { faculty: "理学部"; @@ -96,6 +103,7 @@ export type ScienceFacultyValue = year: UndergraduateYear; }; +/** 工学部 @see https://www.eng.shizuoka.ac.jp/department/ */ export type EngineeringFacultyValue = | { faculty: "工学部"; @@ -130,6 +138,7 @@ export type EngineeringFacultyValue = year: UndergraduateYear; }; +/** 農学部 @see https://www.agr.shizuoka.ac.jp/ */ export type AgricultureFacultyValue = | { faculty: "農学部"; @@ -143,6 +152,7 @@ export type AgricultureFacultyValue = year: UndergraduateYear; }; +/** グローバル共創科学部 @see https://www.gkk.shizuoka.ac.jp/outline/courses/ */ export type GlobalCoCreationFacultyValue = { faculty: "グローバル共創科学部"; department: "グローバル共創科学科"; @@ -153,23 +163,25 @@ export type GlobalCoCreationFacultyValue = { year: UndergraduateYear; }; +/** 地域創造学環 @see https://www.srd.shizuoka.ac.jp/ */ export type RegionalDevelopmentValue = { faculty: "地域創造学環"; year: UndergraduateYear; }; export type UndergraduateAffiliationValue = - | HumanitiesFacultyValue - | EducationFacultyValue - | InformaticsFacultyValue - | ScienceFacultyValue - | EngineeringFacultyValue - | AgricultureFacultyValue - | GlobalCoCreationFacultyValue - | RegionalDevelopmentValue; + | HumanitiesFacultyValue // 人文社会科学部 + | EducationFacultyValue // 教育学部 + | InformaticsFacultyValue // 情報学部 + | ScienceFacultyValue // 理学部 + | EngineeringFacultyValue // 工学部 + | AgricultureFacultyValue // 農学部 + | GlobalCoCreationFacultyValue // グローバル共創科学部 + | RegionalDevelopmentValue; // 地域創造学環 // ── 修士課程(Master) ── +/** 人文社会科学研究科 @see https://www.hss.shizuoka.ac.jp/ghss/ */ export type HumanitiesMasterValue = | { school: "人文社会科学研究科"; @@ -190,6 +202,7 @@ export type HumanitiesMasterValue = year: MasterYear; }; +/** 総合科学技術研究科 @see https://www.shizuoka.ac.jp/subject/graduate/stg/ */ export type IntegratedSciTechMasterValue = | { school: "総合科学技術研究科"; @@ -230,18 +243,20 @@ export type IntegratedSciTechMasterValue = year: MasterYear; }; +/** 山岳流域研究院 @see https://www.igsmw.shizuoka.ac.jp/ */ export type MountainWatershedValue = { school: "山岳流域研究院"; year: MasterYear; }; export type MasterAffiliationValue = - | HumanitiesMasterValue - | IntegratedSciTechMasterValue - | MountainWatershedValue; + | HumanitiesMasterValue // 人文社会科学研究科 + | IntegratedSciTechMasterValue // 総合科学技術研究科 + | MountainWatershedValue; // 山岳流域研究院 // ── 博士課程(Doctoral) ── +/** 創造科学技術大学院 @see https://gsst.shizuoka.ac.jp/ */ export type CreativeSciTechDoctoralValue = { school: "創造科学技術大学院"; major: @@ -253,12 +268,14 @@ export type CreativeSciTechDoctoralValue = { year: DoctoralYear; }; +/** 教育学研究科(博士) @see https://subdev.ed.shizuoka.ac.jp/ */ export type EducationDoctoralValue = { school: "教育学研究科"; major: "共同教科開発学専攻"; year: DoctoralYear; }; +/** 光医工学研究科 @see https://www.cmmp.shizuoka.ac.jp/ */ export type OptoBiomedicalDoctoralValue = { school: "光医工学研究科"; major: "光医工学共同専攻"; @@ -266,12 +283,13 @@ export type OptoBiomedicalDoctoralValue = { }; export type DoctoralAffiliationValue = - | CreativeSciTechDoctoralValue - | EducationDoctoralValue - | OptoBiomedicalDoctoralValue; + | CreativeSciTechDoctoralValue // 創造科学技術大学院 + | EducationDoctoralValue // 教育学研究科 + | OptoBiomedicalDoctoralValue; // 光医工学研究科 // ── 専門職学位課程(Professional) ── +/** 教育学研究科(専門職) @see https://dapse2.ed.shizuoka.ac.jp/ */ export type ProfessionalAffiliationValue = { school: "教育学研究科"; major: "教育実践高度化専攻"; From 1fcef644581f081fd1623ad44c2491a8cb3e03df Mon Sep 17 00:00:00 2001 From: KinjiKawaguchi Date: Thu, 12 Mar 2026 16:16:41 +0900 Subject: [PATCH 4/7] =?UTF-8?q?refactor:=20=E6=9C=AA=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E3=81=AEInvalidAffiliationException=E3=82=92=E5=89=8A=E9=99=A4?= =?UTF-8?q?=E3=81=97=E3=80=81=E7=B5=84=E7=B9=94=E6=A7=8B=E9=80=A0=E5=9E=8B?= =?UTF-8?q?=E3=81=ABJSDoc=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 型レベルで安全性を保証する設計のため不要だったInvalidAffiliationExceptionを削除。 universityStructure.tsのフィールドにユビキタス言語との対応を示すJSDocを追加。 Co-Authored-By: Claude Opus 4.6 --- src/domain/exceptions/DomainExceptions.ts | 8 ----- .../shared/affiliation/universityStructure.ts | 34 +++++++++++++++++-- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/domain/exceptions/DomainExceptions.ts b/src/domain/exceptions/DomainExceptions.ts index dc68586..339e71a 100644 --- a/src/domain/exceptions/DomainExceptions.ts +++ b/src/domain/exceptions/DomainExceptions.ts @@ -104,14 +104,6 @@ export class ExhibitHasMemberException extends DomainException { this.name = "ExhibitHasMemberException"; } } - -export class InvalidAffiliationException extends DomainException { - constructor(detail: string) { - super(`無効な所属情報です: ${detail}`); - this.name = "InvalidAffiliationException"; - } -} - export class InvalidStudentIdException extends DomainException { constructor(studentId: string) { super( diff --git a/src/domain/shared/affiliation/universityStructure.ts b/src/domain/shared/affiliation/universityStructure.ts index e9401af..a61b9c2 100644 --- a/src/domain/shared/affiliation/universityStructure.ts +++ b/src/domain/shared/affiliation/universityStructure.ts @@ -2,8 +2,17 @@ * 静岡大学の組織構造定義 * * 各学部・研究科の階層を型レベルで表現する。 - * 分類子は実際の名称に対応: Faculty(学部), Department(学科), Program(課程), - * Major(専攻), Course(コース), Subspecialty(専修) + * + * フィールドとユビキタス言語の対応: + * - `faculty` — 学部(学部課程の組織単位) + * - `school` — 研究科・大学院(大学院課程の組織単位) + * - `department` — 学科 + * - `program` — 課程(例: 学校教育教員養成課程) + * - `major` — 専攻 + * - `course` — コース(専門分野の細分化) + * - `subspecialty` — 専修(コースよりさらに細分化された専門領域) + * - `enrollmentType` — 入学区分(昼間コース / 夜間主コース) + * - `year` — 在学年次 * * @see https://www.shizuoka.ac.jp/subject/ * @see https://www.ed.shizuoka.ac.jp/applicants/about/organization/ @@ -21,25 +30,38 @@ export type ProfessionalYear = 1 | 2; /** 人文社会科学部 @see https://www.hss.shizuoka.ac.jp/ */ export type HumanitiesFacultyValue = | { + /** 学部 */ faculty: "人文社会科学部"; + /** 入学区分 */ enrollmentType: "昼間コース"; + /** 学科 */ department: "社会学科" | "言語文化学科" | "法学科" | "経済学科"; + /** 在学年次 */ year: UndergraduateYear; } | { + /** 学部 */ faculty: "人文社会科学部"; + /** 入学区分 */ enrollmentType: "夜間主コース"; + /** 学科 */ department: "法学科" | "経済学科"; + /** 在学年次 */ year: UndergraduateYear; }; /** 教育学部 @see https://www.ed.shizuoka.ac.jp/applicants/about/organization/ */ export type EducationFacultyValue = | { + /** 学部 */ faculty: "教育学部"; + /** 課程 */ program: "学校教育教員養成課程"; + /** 専攻 */ major: "発達教育学専攻"; + /** 専修 */ subspecialty: "教育実践学専修" | "教育心理学専修" | "幼児教育専修"; + /** 在学年次 */ year: UndergraduateYear; } | { @@ -80,8 +102,11 @@ export type EducationFacultyValue = /** 情報学部 @see https://www.inf.shizuoka.ac.jp/ */ export type InformaticsFacultyValue = { + /** 学部 */ faculty: "情報学部"; + /** 学科 */ department: "情報科学科" | "行動情報学科" | "情報社会学科"; + /** 在学年次 */ year: UndergraduateYear; }; @@ -99,6 +124,7 @@ export type ScienceFacultyValue = } | { faculty: "理学部"; + /** コース */ course: "創造理学コース"; year: UndergraduateYear; }; @@ -184,9 +210,13 @@ export type UndergraduateAffiliationValue = /** 人文社会科学研究科 @see https://www.hss.shizuoka.ac.jp/ghss/ */ export type HumanitiesMasterValue = | { + /** 研究科・大学院 */ school: "人文社会科学研究科"; + /** 専攻 */ major: "臨床人間科学専攻"; + /** コース */ course: "臨床心理学コース" | "臨床人間科学コース"; + /** 在学年次 */ year: MasterYear; } | { From 7a570501be6d20607e5052eb2f77e0031d0bf51e Mon Sep 17 00:00:00 2001 From: KinjiKawaguchi Date: Thu, 12 Mar 2026 16:19:20 +0900 Subject: [PATCH 5/7] =?UTF-8?q?refactor:=20=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=83=AC=E3=83=99=E3=83=ABJSDoc=E3=81=8B=E3=82=89?= =?UTF-8?q?=E6=95=99=E8=82=B2=E5=AD=A6=E9=83=A8=E5=9B=BA=E6=9C=89=E3=81=AE?= =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=AF=E3=82=92=E9=99=A4=E5=8E=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 教育学部のURLは EducationFacultyValue の型JSDocに既にあるため重複を解消。 Co-Authored-By: Claude Opus 4.6 --- src/domain/shared/affiliation/universityStructure.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/domain/shared/affiliation/universityStructure.ts b/src/domain/shared/affiliation/universityStructure.ts index a61b9c2..5b15bc9 100644 --- a/src/domain/shared/affiliation/universityStructure.ts +++ b/src/domain/shared/affiliation/universityStructure.ts @@ -15,7 +15,6 @@ * - `year` — 在学年次 * * @see https://www.shizuoka.ac.jp/subject/ - * @see https://www.ed.shizuoka.ac.jp/applicants/about/organization/ */ // ── 年次 ── From 17939eb7450f0f1852d3a97982c094cceaf16ef4 Mon Sep 17 00:00:00 2001 From: KinjiKawaguchi Date: Thu, 12 Mar 2026 16:21:48 +0900 Subject: [PATCH 6/7] =?UTF-8?q?style:=20DomainExceptions.ts=E3=81=AE?= =?UTF-8?q?=E7=A9=BA=E8=A1=8C=E3=82=92develop=E3=81=A8=E4=B8=80=E8=87=B4?= =?UTF-8?q?=E3=81=95=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- src/domain/exceptions/DomainExceptions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/domain/exceptions/DomainExceptions.ts b/src/domain/exceptions/DomainExceptions.ts index 339e71a..0168334 100644 --- a/src/domain/exceptions/DomainExceptions.ts +++ b/src/domain/exceptions/DomainExceptions.ts @@ -104,6 +104,7 @@ export class ExhibitHasMemberException extends DomainException { this.name = "ExhibitHasMemberException"; } } + export class InvalidStudentIdException extends DomainException { constructor(studentId: string) { super( From 752e2ed188dd86eec3cd44c6884fc17da835ed6f Mon Sep 17 00:00:00 2001 From: KinjiKawaguchi Date: Thu, 12 Mar 2026 16:22:44 +0900 Subject: [PATCH 7/7] =?UTF-8?q?docs:=20Affiliation=E5=9E=8B=E3=81=AB?= =?UTF-8?q?=E3=83=A6=E3=83=93=E3=82=AD=E3=82=BF=E3=82=B9=E8=A8=80=E8=AA=9E?= =?UTF-8?q?=E3=80=8C=E6=89=80=E5=B1=9E=E3=80=8D=E3=81=A8=E3=81=AE=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=E3=82=92JSDoc=E3=81=A7=E6=98=8E=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- src/domain/shared/affiliation/Affiliation.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/domain/shared/affiliation/Affiliation.ts b/src/domain/shared/affiliation/Affiliation.ts index 846832d..610446f 100644 --- a/src/domain/shared/affiliation/Affiliation.ts +++ b/src/domain/shared/affiliation/Affiliation.ts @@ -26,6 +26,7 @@ export class ProfessionalAffiliation extends ValueObject