技術筆記-專案架構,我目前的最佳實行 (python api server project)

更新 發佈閱讀 12 分鐘

開發生涯,不斷堆加著程式碼,總有那麼一天,只是不經意地修改,卻...突然壞掉了!然後,修很久都修不好。這說明了一件重要的事:專案結構脆弱;隨著認知程度和專案規模的變大,標準也跟著一直提升。而這就是系統要「轉骨」的時候了,為了長治久安,必須進行重構,才能迎接下一階段的成長。以下紀錄我目前的最佳實行。

source control 相關

根目錄的第一個東西,隱藏目錄 .git 和隱藏檔 .gitignore。這是為了 source control。

python 虛擬環境相關

第二個東西是隱藏目錄 .venv (個人習慣的命名),專為此專案的 python 執行環境,相關的操作指令如下:

# 以下指令在 zsh 終端機畫面,在專案根目錄執行,% 為提示符號
# 本專案指定使用 python3.11
% python3.11 -m venv ​.venv
% source .vnev/bin/activate

# 之後進入 python 虛擬環境,提示符號變為 (.venv)%
(.venv)% pip install --upgrade pip setuptools wheel
(.venv)% pip install -r requirements.txt

# requirements.txt 是另一個必須在根目錄的檔案,產生的指令如下:
(.venv)% pip freeze > requirements.txt

以上是專案最底層的資訊,非常重要,若有差錯,日後都是傷筋動骨的事,所有開發和執行的動作,都是基於此虛擬環境。

os 層級的檢查

比虛擬環境更底層的,是 os,在繼續發展之前,需要確認根基的穩固,相關指令如下:

# 看看目前的 python 版本,是什麼平台的​
(.vnev)% python -c "import platform; print(platform.machine())"
# 以我的環境為例,應該會輸出 arm64,如果是 x86,代表用到模擬的 x86 環境,
# 效率較差,必須重來。以下紀錄整個重來的過程。

# 很有可能,連用來安裝 python 的 brew 就已經是 x86 版本,那就會一路錯下去
# 重裝 arm 版本的 Homebrew,開一個乾淨的 terminal 執行以下指令
% /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 檢查 brew 是哪一個 brew
% which brew
# 應該是 /opt/homebrew/bin/brew
# 也可以看看系統有多少 brew
% which -a brew
# 有可能多版本並存如下
# /opt/homebrew/bin/brew
# /usr/local/bin/brew​
# 這時就要看誰排在前面,若要確認先用 arm 版本的 brew,需改 ~/.zshrc
% nano ~/.zshrc
# 把這行加在最上面
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:$PATH"
# 執行一次 .zshrc,再檢查一次 which brew
% source ~/.zshrc​

# 用乾淨的 brew 安裝 pyenv
% brew install pyenv
# 用 pyenv 安裝 python
% pyenv install 3.11.14
% which python3.11
% python3.11 --version

需要以上嚴格的程序,確認正確的 python 版本。

cache 目錄

程式運行中,需要高頻率反覆存取的東西,而原始資料卻不會一直變動者,適合存成本地檔案。如「日 K 線」就是典型,昨天以前的資料,在當日之間是不會變動的,但計算波動與報酬,卻會經常用到,適合存起來快取。

wheel 目錄

有些套件,並沒有公開到外面的資料庫,如本系統使用的券商 api,是直接提供 .whl 檔,當佈署雲端時也是必須包含的。此外,也有可能再開 resource 目錄,反正只要 deploy 時要一併送上雲端的檔案,都可以開在專案根目錄,方便參用。

流程自動化所需 shell script file

如「佈署到雲端」的一系列動作,可以寫成 deploy.sh,放在專案根目錄。

#!/bin/bash
set -e # stop on error

echo "👉 Cleaning old package..."
rm -f myapp.zip

echo "👉 Packing all source files into myapp.zip..."
zip -r myapp.zip . \
-x ".venv/*" \
-x ".vscode/*" \
-x ".gitiqnore" \
-x "**/__pycache__/*" \
-x ".git/*" \
-x "candles_cache/*" \
-x "log/*" \
-x "**/log/*"

echo "👉 Adding cached price snapshots to the package..."
zip -r myapp.zip candles_cache/price_snapshot*
zip -r myapp.zip candles_cache/symbol_names*

echo "👉 Deploying to Azure App Service..."
az webapp deploy \
--resource-group aaabbb \
--name xxxyyy \
--src-path myapp.zip \
--type zip \
--restart true \
--clean true

echo "🎉 Deployment complete!"

原始碼

重點來了,因為專案根目錄已經太多資料了,我所有的原始碼都放在 src 目錄下面,且為了專案檔案間可以互相 import,最好是遵循正規的 package 規範。一個 package 就是一個目錄,為了有基準點,我把 src 也變成一個 package,所需動作就是在 src 目錄中新增一個檔案 __init __.py,檔案內容就先空著。

src 目錄中直接放置的程式碼很少,主要是作為入口的 main.py,和作為路由器的 router.py,其餘大量程式主要分為兩類,分別放置兩個目錄,controller and service,他們也都是 package,這樣 import 的規則就很簡單,全部採用「相對路徑」:

# main.py 引用同目錄的 newrouter.py:
from .newrouter import newrouter

# 在某個 service 目錄中,引用同一層的其他 service,也是同樣的方式:
from .firestoreService import FirestoreService

# controller 引用 service 的方式,從上兩層為參考點:
from ..newservice.fubonService import FubonService

package 的設置,大幅提升了專案的可攜性,在 deploy to cloud 時,降低踩雷機率。以前胡亂 import,可以跑就先撐著,苟延殘喘,某一天一定會爆,那時就被迫要做這件事了。

啟動 fastapi server 的方式

注意必須在虛擬環境提示符之下 (.venv)%,在專案根目錄,輸入:
uvicorn src.main:app --reload

重點就是,以「專案根目錄」為參考點,最清晰明瞭,不易出錯。以上 src 就是 package name,main 就是模組,app 就是 server instance,所以在 main.py 必須有一行:
app = FastAPI()

# main.py,這是程式進入點​
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from .newrouter import newrouter

app = FastAPI()

origins = [
"http://localhost:5173", # Vite local frontend
"http://127.0.0.1:5173",
"https://newman-portfolio.azurewebsites.net", # Azure frontend
]

app.add_middleware(
CORSMiddleware,
allow_origins=origins, # domains allowed, ["x"] for allow all
allow_credentials=True,
allow_methods=["*"], # GET, POST, etc.
allow_headers=["*"], # Authorization, Content-Type, ...
)

app.include_router(newrouter, prefix="/api")

部署到 azure app service

azure app service 中,重要且必要的設定如下:

  • Stack setting:選擇正確的 python 版本
  • Startup command:uvicorn src.main:app --host 0.0.0.0 --port 8000

在 deploy script 中會用到的重要識別資料:resource group name and name。

部署時最常用到的工具,就是 Log stream and SSH。

總結

以上紀錄的重點,基本上等同於「踩過的雷」,特此紀錄,以免再犯。

project/
├─ .venv/
├─ .git
├─ .gitignore
├─ cache/
├─ wheels/
├─ src/
│ ├─ __init__.py
│ ├─ main.py
│ ├─ newrouter.py
│ ├─ controller/
│ │ ├─ __init__.py
│ │ ├─ stockController.py
│ ├─ service/
│ │ ├─ __init__.py
│ │ ├─ fubonservice.py
├─ deploy.sh
├─ requirements.txt

Newman 2026/2/5

導覽頁:紐曼的技術筆記-索引

導覽頁:精明管家



留言
avatar-img
newman的沙龍
30會員
147內容數
漫步是一種境界。
newman的沙龍的其他內容
2025/11/19
沒錯,我又重來了。近期又重新享受了一波,沈浸在程式開發的快樂。好像已過 20 年想做的事,又重新濃縮式的跑了一遍,速度是幾十倍速的飛快。 啓後必有承先 因著對資訊技術的愛好,五年來我得到充分的自由,可以天馬行空的嘗試探索。雖然沒有企業或團隊資源的支持,但可玩的東西已經很多了。 記得那個自由的起
Thumbnail
2025/11/19
沒錯,我又重來了。近期又重新享受了一波,沈浸在程式開發的快樂。好像已過 20 年想做的事,又重新濃縮式的跑了一遍,速度是幾十倍速的飛快。 啓後必有承先 因著對資訊技術的愛好,五年來我得到充分的自由,可以天馬行空的嘗試探索。雖然沒有企業或團隊資源的支持,但可玩的東西已經很多了。 記得那個自由的起
Thumbnail
2025/10/21
分散之必須 「分散」是消除風險的重要方法。當我們把資金分散到多個標的,並不能真正安心,因為許多標的存在著相關性。比如說同產業的多標的,容易同漲同跌,導致雖然標的分散了,實際上風險卻沒有分散。分散的方法可以基於基本面的知識,也可以基於量化分析,而最好是兩者相輔相成。比如說最近華邦電與力積電一同飆漲,
Thumbnail
2025/10/21
分散之必須 「分散」是消除風險的重要方法。當我們把資金分散到多個標的,並不能真正安心,因為許多標的存在著相關性。比如說同產業的多標的,容易同漲同跌,導致雖然標的分散了,實際上風險卻沒有分散。分散的方法可以基於基本面的知識,也可以基於量化分析,而最好是兩者相輔相成。比如說最近華邦電與力積電一同飆漲,
Thumbnail
2025/09/16
又來搞認證了,久久搞一次,每次都卡關,記得上一次做 web 前端也弄很久,有留下記錄,是有點久的舊文: 技術-前端類-angular 網頁整合 google login (2022/4/20) 認證,或指廣泛的關於安全性的議題,超級重要的,但在小咖咖階段就還好,沒有人會想來攻擊我,沒有肉啊。但當
Thumbnail
2025/09/16
又來搞認證了,久久搞一次,每次都卡關,記得上一次做 web 前端也弄很久,有留下記錄,是有點久的舊文: 技術-前端類-angular 網頁整合 google login (2022/4/20) 認證,或指廣泛的關於安全性的議題,超級重要的,但在小咖咖階段就還好,沒有人會想來攻擊我,沒有肉啊。但當
Thumbnail
看更多
你可能也想看
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
在 FastAPI 開發中,ORM 模型是用來與資料庫進行互動的橋樑。簡單來說,它將Python 的類別 (Class)對應到資料庫的表格 (Table),並將類別的屬性對應到表格的欄位 (Column)。
Thumbnail
在 FastAPI 開發中,ORM 模型是用來與資料庫進行互動的橋樑。簡單來說,它將Python 的類別 (Class)對應到資料庫的表格 (Table),並將類別的屬性對應到表格的欄位 (Column)。
Thumbnail
在 FastAPI 的依賴注入中,yield 主要用於建立「帶有清理功能的依賴項」。簡單來說,使用 return 的依賴項只負責「創建」資源,而使用 yield 的依賴項則能同時處理「準備資源」與「清理資源」。
Thumbnail
在 FastAPI 的依賴注入中,yield 主要用於建立「帶有清理功能的依賴項」。簡單來說,使用 return 的依賴項只負責「創建」資源,而使用 yield 的依賴項則能同時處理「準備資源」與「清理資源」。
Thumbnail
這篇文章將教你如何使用 FastAPI 的「依賴注入 (Dependency Injection)」,學會依賴注入後,你不再需要重複複製貼上相同的檢查邏輯,或是手動建立資料庫連線。
Thumbnail
這篇文章將教你如何使用 FastAPI 的「依賴注入 (Dependency Injection)」,學會依賴注入後,你不再需要重複複製貼上相同的檢查邏輯,或是手動建立資料庫連線。
Thumbnail
這篇文章將教你如何使用 FastAPI 的 Query 與 Path 函式,對 API 的輸入參數進行嚴格的格式驗證,並且為 API 文件添加描述資訊。
Thumbnail
這篇文章將教你如何使用 FastAPI 的 Query 與 Path 函式,對 API 的輸入參數進行嚴格的格式驗證,並且為 API 文件添加描述資訊。
Thumbnail
這篇文章將教你如何在 FastAPI 中宣告與使用查詢參數 (Query Parameters),讓你能實現資料過濾、搜尋與分頁等功能。
Thumbnail
這篇文章將教你如何在 FastAPI 中宣告與使用查詢參數 (Query Parameters),讓你能實現資料過濾、搜尋與分頁等功能。
Thumbnail
這篇文章將教你如何在 FastAPI 中宣告並讀取路徑參數 (Path Parameters),你將學會如何透過 URL 傳遞變數給伺服器,實現取得特定用戶資料或商品資訊等功能。
Thumbnail
這篇文章將教你如何在 FastAPI 中宣告並讀取路徑參數 (Path Parameters),你將學會如何透過 URL 傳遞變數給伺服器,實現取得特定用戶資料或商品資訊等功能。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News