
【IT 架構筆記 005】ABP Framework + Blazor Server:企業級 ERP 系統的分層架構實踐
以 ABP Framework 為基礎,搭配 Blazor Server 前端與 Entity Framework Core,實踐 DDD 領域驅動設計的企業級 ERP 系統架構。本文拆解各層職責、技術選型理由,以及與傳統 MVC 架構的差異比較。
WRITTEN BY

- Name
- Harry Chang
在建構企業級製造業 ERP 系統時,技術架構的選型攸關系統的長期可維護性與擴充性。本文記錄一套以 ABP Framework 8.x + Blazor Server + Entity Framework Core 為核心的實戰架構,並深入解析為何採用 DDD 分層設計,以及各層的實際職責。
.NET 網頁技術發展史
在理解現代架構之前,先看 .NET 網頁技術走過的路:
| 年代 | 技術 | 模式 | 特色 |
|---|---|---|---|
| 2002 | ASP.NET WebForms | 事件驅動 | 類似 VB 的拖拉控制項,Button、GridView、事件雙擊寫邏輯,畫面與邏輯混在一起 |
| 2009 | ASP.NET MVC | MVC 三層 | 畫面(View)、邏輯(Controller)、資料(Model)明確分離,Razor 語法撰寫 HTML |
| 2012 | ASP.NET Web API | RESTful API | 純後端 API,前端改用 JavaScript 框架(jQuery、AngularJS)呼叫 |
| 2016 | ASP.NET Core MVC | 跨平台 MVC | 重寫為跨平台版本,支援 Linux 部署,效能大幅提升 |
| 2019 | Blazor Server | 元件化 | C# 取代 JavaScript,畫面邏輯透過 SignalR 在伺服器執行,瀏覽器只收 HTML |
| 2020 | Blazor WebAssembly | 元件化 | C# 編譯為 WebAssembly 直接在瀏覽器執行,不需要伺服器即時連線 |
| 現在 | ABP + Blazor Server | DDD 分層 | 企業級框架封裝多租戶、權限、稽核,搭配 Blazor 前端全 C# 開發 |
核心演進脈絡:
- WebForms → MVC:從「拖拉控制項」走向「程式碼明確分層」
- MVC → Web API + JS 前端:前後端徹底分離,各自獨立部署
- Web API → Blazor:把 JavaScript 換回 C#,統一語言降低切換成本
- Blazor → ABP + Blazor:在 Blazor 之上加企業級框架,解決多租戶、權限等共通需求
技術棧總覽
| 層次 | 技術 | 版本 |
|---|---|---|
| 前端框架 | Blazor Server | .NET 8 |
| UI 元件庫 | Ant Design Blazor | 1.6.x |
| 後端框架 | ABP Framework | 8.3.x |
| ORM | Entity Framework Core | 8.x |
| 資料庫 | SQL Server | 2022 |
| 認證授權 | OpenIddict | 內建於 ABP |
| 主題 | LeptonX Lite | ABP 官方 |
| 部署環境 | Windows Server + IIS | - |
為什麼選擇 ABP Framework
ABP Framework 是 .NET 生態系中最完整的企業級應用框架,它內建解決了大多數企業系統都需要處理的橫切關注點:
- 多租戶(Multi-tenancy):同一套系統供多個工廠/子公司使用,資料完全隔離
- 權限管理:細粒度的功能權限定義與檢查,內建 UI 管理介面
- 稽核日誌:自動記錄誰、何時、對哪筆資料做了什麼變更
- 模組化:業務功能可以獨立成模組,各自擁有完整的分層結構
對於需要對接多個外部系統(ERP、WMS、簽核引擎)的製造業系統,這些能力若從零實作需耗費大量時間,ABP 讓團隊可以聚焦在業務邏輯本身。
DDD 分層架構詳解
ABP Framework 強制採用 Domain-Driven Design(DDD,領域驅動設計) 的分層架構。整個解決方案由以下幾層構成:
第一層:Domain Layer(領域層)
這是整個系統最核心的一層,定義業務實體與業務規則。
// 以訂單主檔為例
public class OrdHeader : FullAuditedAggregateRoot<Guid>, IMultiTenant, IMustHavePlant
{
public string OrderNo { get; set; }
public string Status { get; set; } = "Draft";
public DateTime DeliveryDate { get; set; }
public ICollection<OrdLine> Lines { get; set; } = new List<OrdLine>();
}
關鍵概念:
- AggregateRoot:聚合根,代表一個完整的業務概念(一張訂單 + 其所有明細)
- FullAudited:ABP 自動注入建立時間、建立者、修改時間、修改者、軟刪除
- IMultiTenant:自動過濾資料,每個租戶只看得到自己的訂單
- IMustHavePlant:自訂介面,強制每筆資料標記所屬工廠
Domain.Shared 子層存放常數、列舉與跨層共用的基礎定義,不依賴任何其他層。
第二層:Application.Contracts Layer(應用合約層)
定義「系統能做什麼」的公開介面,是畫面與邏輯之間的合約。
public interface IOrdHeaderAppService :
ICreateUpdateAppService<OrdHeaderDto, Guid, CreateUpdateOrdHeaderDto>
{
Task<PagedResultDto<OrdHeaderDto>> GetListAsync(OrdHeaderListInput input);
Task ConfirmAsync(Guid id);
}
這一層只有介面與 DTO(Data Transfer Object),沒有任何實作邏輯。好處是 UI 層只依賴介面,未來實作可以替換而不影響畫面。
第三層:Application Layer(應用服務層)
實作 Contracts 定義的介面,負責編排業務流程。
public class aaaAppService : AppService, aaaAppService
{
public async Task ConfirmAsync(Guid id)
{
await AuthorizationService.CheckAsync(OrdersPermissions.Confirm);
var order = await _repository.GetAsync(id);
order.Status = "Confirmed";
order.ConfirmDate = Clock.Now;
await _repository.UpdateAsync(order);
}
}
這層的職責:驗證權限、呼叫 Domain 層、協調多個服務、觸發事件。不包含資料庫細節,也不包含畫面細節。
第四層:EntityFrameworkCore Layer(基礎建設層)
將 Domain 實體映射到資料庫資料表,處理所有持久化細節。
builder.Entity<OrdHeader>(b =>
{
b.ToTable("OrdHeaders");
b.Property(x => x.OrderNo).IsRequired().HasMaxLength(30);
b.HasMany(x => x.Lines).WithOne().HasForeignKey(x => x.HeaderId);
});
當 Domain 層的實體新增欄位時,這層產生 Migration 自動更新資料表結構,不需要手寫 SQL。
第五層:Blazor Server UI Layer(展示層)
使用 Razor Component 撰寫畫面,透過注入的 AppService 介面與後端溝通。
@inject IOrdHeaderAppService OrderService
@code {
private List<OrdHeaderDto> _items = new();
protected override async Task OnInitializedAsync()
{
var result = await OrderService.GetListAsync(new OrdHeaderListInput());
_items = result.Items.ToList();
}
}
畫面只認識介面(IOrdHeaderAppService),完全不知道底層資料庫是 SQL Server 還是其他資料庫。
模組化設計
除了主應用程式的分層外,複雜的業務領域被拆分為獨立模組,每個模組擁有完整的四層結構:
modules/
├── Orders/ ← 訂單模組
│ ├── Domain/
│ ├── Domain.Shared/
│ ├── Application.Contracts/
│ └── EntityFrameworkCore/
└── stock/ ← 庫存模組
模組的應用服務實作統一放在主應用程式的 Application 層,讓跨模組的業務邏輯可以互相呼叫,同時保持模組定義的獨立性。
Blazor Server vs 傳統前後端分離
採用 Blazor Server 而非 React + API 的關鍵考量:
| 考量點 | Blazor Server | React + API |
|---|---|---|
| 語言統一 | 前後端皆為 C# | 前端 JS,後端 C# |
| 即時互動 | SignalR 自動處理 | 需自行實作 WebSocket |
| ABP 整合 | 官方完整支援 | 需額外整合 |
| 適用場景 | 內部系統、固定網路環境 | 對外系統、行動裝置 |
| 人才市場 | 較少 | 較多 |
對於使用者在公司內網操作的企業內部系統,Blazor Server 的 SignalR 連線穩定性足夠,且省去維護兩套語言與 API 文件的成本。
部署架構
開發者本機
↓ git push(內網 Bonobo Git Server)
Windows Server
├── IIS(aaa_prod / aaa_test 兩個站台)
├── dotnet publish -c Release
└── 設定檔分離:appsettings.json(版控)+ appsettings.secrets.json(不版控)
設定檔採雙層設計:一般設定進版控,含密碼的 secrets 檔案僅存於各環境伺服器,不進入 Git。
多新增一個功能需要改幾個地方
這是 DDD 分層架構最常被詢問的問題。以新增一個欄位為例:
| 步驟 | 檔案 | 說明 |
|---|---|---|
| 1 | Domain/Entities/OrdHeader.cs | 新增屬性 |
| 2 | Application.Contracts/Dto/OrdHeaderDto.cs | DTO 對應 |
| 3 | EntityFrameworkCore Migration | 資料表新增欄位 |
| 4 | Application/OrdHeaderAppService.cs | 業務邏輯調整(如需要) |
| 5 | Blazor/Pages/Orders/OrderEdit.razor | 畫面新增輸入框 |
五個地方看似繁瑣,但每個地方職責單一、邏輯清晰。相較於在一個大檔案裡混雜所有邏輯,長期維護成本反而更低。
小結
ABP Framework + Blazor Server 的組合,本質上是用較高的初始架構複雜度換取長期的可維護性與擴充性。對於業務規則複雜、需要對接多個外部系統、有多租戶需求的企業內部系統,這套架構提供了一個清晰的邊界與規範,讓團隊在系統規模成長後仍然能夠有序地擴展功能。
若是小型內部工具或快速驗證的原型,則可考慮 ABP 的 App No Layers 範本,以更少的分層換取更快的開發速度。
參考資源
官方文件
| 資源 | 說明 |
|---|---|
| ABP Framework 官網 | 框架文件、教學、範本 |
| ABP GitHub | 原始碼,12k+ stars |
| ABP DDD 指南 | DDD 分層設計官方說明 |
| Blazor Server 文件 | Microsoft 官方 Blazor 文件 |
| Ant Design Blazor | UI 元件庫文件 |
值得參考的 GitHub 專案
| 專案 | 技術棧 | 說明 |
|---|---|---|
| ant-design-blazor-abp | ABP + Blazor + Ant Design | 與本文架構完全一致的範本專案 |
| abpframework/abp | ABP Framework | 框架本體,含 BookStore 教學範例 |
| EasyAbp/awesome-abp | ABP 生態系 | 社群整理的模組與資源清單 |