跳至主要内容

Feature 模組架構指南

🛠️ 這是給開發者/工程師的技術文件(程式碼模組架構)。一般使用者看 快速開始 就夠了。

📋 概述

本專案採用 Feature-based Organization(功能導向組織),每個功能模組都是獨立、可插拔的單元。每個 feature 都有一個 Barrel File (index.ts) 定義其公開 API,強制執行封裝並防止功能間的耦合。


🏗️ 架構規則

✅ 正確做法:從 feature 的公開 API 匯入

// ✅ 正確 - 使用 barrel file
import { MedicalChatFeature } from '@/features/medical-chat'
import { DataSelectionDrawer } from '@/features/data-selection'
import { AllergiesCard, VitalsCard } from '@/features/clinical-summary'
import { AuthDialog, useAuthDialog } from '@/features/auth'

❌ 錯誤做法:匯入內部實作

// ❌ 錯誤 - 不要直接存取內部檔案
import MedicalChat from '@/features/medical-chat/components/MedicalChat'
import { useStreamingChat } from '@/features/medical-chat/hooks/useStreamingChat'

❌ 錯誤做法:跨 feature 依賴

// ❌ 錯誤 - Features 之間不應該相互依賴
import { SomeHook } from '@/features/other-feature/hooks/SomeHook'

📦 Feature 目錄

1. Auth(使用者認證)

Entry Point: @/features/auth

import {
AuthDialog,
AuthStatus,
HeaderAuthButton,
useAuthDialog
} from '@/features/auth'

// Usage
<HeaderAuthButton />
<AuthDialog />

功能

  • Firebase Authentication 整合
  • Google 登入
  • Email/密碼登入
  • Email 驗證
  • 登入狀態管理

2. Chat History(對話歷史)

Entry Point: @/features/chat-history

import { ChatHistoryDrawer } from '@/features/chat-history'

// Usage
<ChatHistoryDrawer />

功能

  • 依病人分類儲存對話
  • Firestore 雲端同步
  • 對話搜尋和管理
  • 繼續先前的對話

3. Medical Chat(AI 對話)

Entry Point: @/features/medical-chat

import { MedicalChatFeature } from '@/features/medical-chat'

// Usage
<MedicalChatFeature />

功能

  • 直接提問,依問題自行查詢目前病人的 FHIR 資料
  • 支援 OpenAI、Gemini、Anthropic 等可用模型
  • 提示範本庫與自訂範本
  • 語音錄製和轉錄
  • 對話歷史整合

4. Clinical Insights(自訂摘要內嵌模組)

Runtime: @/features/clinical-insights/*,由 @/features/medical-summary/Feature 組合

// 不是 right-panel 獨立分頁。
// MedicalSummaryFeature 透過 ClinicalInsightsRuntimeProvider,
// 在「自訂摘要」TabsContent 內呈現 CustomInsightModulesSection。

功能

  • 自訂提示詞與摘要模組
  • 最多啟用 5 個模組顯示於醫療摘要的「自訂」子分頁
  • 共用臨床洞察設定、執行與快取 provider
  • 由模組管理抽屜新增、編輯、排序與重跑

5. Data Selection(AI 資料範圍抽屜)

Entry Point: @/features/data-selection

import { DataSelectionDrawer } from '@/features/data-selection'

// 由 MedicalSummaryFeature 的「設定 → 資料範圍」開啟
<DataSelectionDrawer open={open} onOpenChange={setOpen} />

功能

  • 不是 right-panel 獨立分頁
  • 初診/追蹤/自訂等資料範圍範本
  • 依病人、就診、報告、用藥、文件細調範圍
  • 顯示估算 token 與模型上下文占比
  • 標準摘要與自訂摘要共用;臨床對話按問題自行查詢,IPS 匯出另有 scope panel

6. Prompt Gallery(提示範本庫)

Entry Point: @/features/prompt-gallery

import {
PromptGalleryDialog,
usePromptGallery
} from '@/features/prompt-gallery'

// Usage
<PromptGalleryDialog />

功能

  • 瀏覽社群共享的提示範本
  • 依類型、專科、標籤篩選
  • 分享自己的提示範本
  • 使用計數追蹤

7. Settings(設定)

Entry Point: @/features/settings

import { SettingsFeature } from '@/features/settings'

// Usage
<SettingsFeature />

功能

  • AI 偏好設定(模型選擇、API 金鑰)
  • 提示範本管理
  • 自訂摘要模組管理
  • 外觀設定(深色/亮色模式)

8. Clinical Summary(臨床摘要)

Entry Point: @/features/clinical-summary

特殊說明:此 feature 匯出多個卡片元件,支援靈活組合。

import {
AllergiesCard,
DiagnosesCard,
MedListCard,
PatientInfoCard,
ReportsCard,
VisitHistoryCard,
VitalsCard
} from '@/features/clinical-summary'

// Usage - 依需求組合
<div>
<PatientInfoCard />
<VitalsCard />
<MedListCard />
</div>

可用卡片

  • AllergiesCard - 過敏史
  • DiagnosesCard - 診斷/病況
  • MedListCard - 用藥清單
  • PatientInfoCard - 病人基本資料
  • ReportsCard - 診斷報告
  • VisitHistoryCard - 就診紀錄
  • VitalsCard - 生命徵象

9. Medical Calculator(醫療計算機)

Entry Point: @/features/medical-calculator

import MedicalCalculatorFeature from '@/features/medical-calculator/Feature'

// Usage(右側面板分頁)
<MedicalCalculatorFeature />

功能

  • MDCalc 風格的臨床計算工具/評分量表,共 10 類、50+ 個(腎、肝、GI、電解質、心血管、肺、血液、神經、精神、一般)
  • 自動帶入病人數值:檢驗值依 canonical/LOINC/檢體(Observation.specimen)解析後自動填入,顯示原始單位並在維度相符時自動換算(僅在真正無法換算時顯示 ⚠)
  • 每個計算機附「適用時機(When to Use)」與「注意事項(Pearls/Pitfalls)」,結果含風險分層與處置建議
  • 我的最愛、最近使用、依受眾(醫療/民眾)與科別/用途篩選、搜尋
  • 民眾可自填的量表(PHQ-9、GDS-15、Epworth…)
  • 結果可一鍵複製成病歷可貼上的一行摘要

資料驅動架構calculators/(依類別分檔,每個 CalculatorDef 帶純函式 compute)+純模組 list-logic.tsformat.tsautofill-compute.tsresolveInput 為自動帶入的唯一真相來源),154 個單元測試。新增一個計算機=新增一筆 CalculatorDef(+ CALC_TAGSCALC_INFO)。


10. Medical Summary(醫療摘要)

Entry Point: @/features/medical-summary/Feature

import MedicalSummaryFeature from '@/features/medical-summary/Feature'

// Usage(右側面板第一個分頁,開啟病人後的預設分頁)
<MedicalSummaryFeature />

功能

  • 零點擊 AI 簡報:載入病人後自動產生,單頁縱向流
  • 跨院病程摘要:3–5 句敘事,附逐筆對回 FHIR bundle 驗證的引用;查無來源標「未驗證」
  • 用藥安全警示(內嵌)features/proactive-safety-alertsSafetyAlertsPanel 以 embedded 模式呈現,依嚴重度分級密度
  • 跨院時間軸:App 端確定性抽取事件骨架(日期、院所、就診類別),AI 只負責策展與標籤——零幻覺日期/院所
  • 資料涵蓋卡:純計算(零 AI)——日期範圍、院所數、各資源計數與健康存摺涵蓋邊界聲明
  • 雙受眾:醫療人員版/民眾版跟隨全域 audience,各自生成與快取(12 小時)

「臨床洞察」(第 4 節)不再是 right-panel 分頁,而是內嵌於本功能的「自訂摘要」子分頁;資料選擇也由本功能的設定選單開啟。


11. Report Interpretation(報告 AI 翻譯解讀)

Entry Point: @/features/report-interpretation

import { ReportInterpretationButton, ReportInterpretationPanel } from '@/features/report-interpretation'

不是右側面板分頁,而是內嵌在報告/文件卡片標頭的小型子功能——每則報告可一鍵生成忠實中譯 + 白話解讀,面板顯示在原文上方

功能

  • 隨選生成、不預先耗用額度:按鈕按下才呼叫 AI;依 reportId::audience::locale::contentSig 快取,不同呈現位置(列表內/右側面板 dock)共用同一份結果
  • 忠實翻譯優先:譯文是嚴格的忠實轉譯防火牆,解釋只出現在「解讀」欄位(防幻覺);不輸出「建議詢問醫師」清單,直接把答案講清楚
  • 雙受眾:醫師/民眾皆可用,語氣依 useAudience() 調整;免責聲明恆常顯示、不可收合
  • 三個掛載點ReportRow.tsx(單一長文報告/結構化 panel 報告)、DocumentSummaryCard.tsx(出院病摘/IPS 文件)、MultiRegionStudyCard.tsxNarrativeSubCard(健保碼共用卡內的子報告)

🔗 依賴規則

Features 可以依賴:

  • @/src/core/* - 領域實體和用例
  • @/src/application/* - 應用層 hooks 和 providers
  • @/src/infrastructure/* - 基礎設施服務
  • @/src/shared/* - 共用工具和元件
  • @/components/ui/* - UI 元件庫(shadcn/ui)

Features 不可以依賴:

  • @/features/* - 其他 features(絕對禁止

📁 內部結構

每個 feature 遵循以下結構:

features/
feature-name/
├── index.ts # 🚪 公開 API (Barrel File)
├── Feature.tsx # 主要元件
├── components/ # 內部元件
├── hooks/ # 內部 hooks
├── services/ # 內部服務(如有)
├── utils/ # 內部工具函數
└── types/ # 內部類型定義

只有 index.ts 的匯出是公開的。 其他所有內容都是內部實作。


🎯 優勢

  1. 封裝性 - 內部變更不影響使用者
  2. 清楚邊界 - 容易理解什麼是公開 vs 私有
  3. 重構安全 - 可以重組內部結構而不破壞匯入
  4. 防止耦合 - 強制 features 保持獨立
  5. 更好的 Tree-shaking - 打包工具可以優化未使用的程式碼
  6. 可插拔 - 透過 Registry 輕鬆啟用/停用功能

🔌 可插拔架構

左側 Panel(臨床摘要)

Registry 配置src/shared/config/feature-registry.ts

export const CLINICAL_SUMMARY_FEATURES: FeatureConfig[] = [
{
id: 'patient-info',
name: 'Patient Information',
component: PatientInfoCard,
tab: 'patient',
order: 0,
enabled: true,
},
// ...
]

新增功能

  1. 建立功能元件
  2. feature-registry.ts 註冊
  3. 完成!無需修改 Layout

右側 Panel(AI 功能)

Registry 配置src/shared/config/right-panel-registry.ts

export const RIGHT_PANEL_FEATURES: FeatureConfig[] = [
{
id: 'medical-chat',
name: 'Medical Chat',
tabLabel: 'medicalChat',
component: () => null,
order: 0,
enabled: true,
},
// ...
]

🛡️ 強制執行

ESLint 規則

建議加入 ESLint 規則來強制執行這些模式:

{
"rules": {
"no-restricted-imports": [
"error",
{
"patterns": [
{
"group": ["@/features/*/*"],
"message": "Import from feature's index.ts instead: @/features/feature-name"
}
]
}
]
}
}

❓ 常見問題

Q: 如果需要在 features 之間共享功能怎麼辦?

考慮以下選項:

  1. 移到 @/src/shared/* - 用於 UI 元件或工具函數
  2. 移到 @/src/core/* - 用於業務邏輯
  3. 移到 @/src/application/* - 用於應用層級的關注點

絕對不要在 features 之間建立直接依賴。

Q: 如何新增一個新的 feature?

  1. features/ 目錄建立新資料夾
  2. 建立 index.ts barrel file
  3. 建立 Feature.tsx 主要元件
  4. 在適當的 registry 註冊(如果需要)
  5. 匯出公開 API

Q: 可以在 feature 內部使用其他 feature 的元件嗎?

不可以。如果需要共享元件,應該將其移到 @/src/shared/components/@/components/ui/


📚 相關文件


🎯 總結

Feature 模組架構提供:

清楚的邊界:每個 feature 都是獨立單元
封裝性:內部實作細節隱藏
可維護性:容易理解和修改
可擴展性:透過 Registry 輕鬆新增功能
重構安全:內部變更不影響外部

遵循這些規則可以保持程式碼庫的整潔和可維護性。