在討論軟體架構時,我們常聽到「單體架構」、「微服務架構」、「MVC」、「Clean Architecture」、「DDD」等術語,但你是否曾疑惑:這些概念之間有什麼關係?為什麼有些看起來很相似,卻又在不同層面被討論?

其實,軟體架構可以分為兩個層次:架構風格(Architecture Style)和架構模式(Architecture Pattern)。架構風格描述的是系統的部署和組織方式,例如單體、微服務;而架構模式描述的是程式碼的組織和分層方式,例如 MVC、Clean Architecture。它們並非互斥,而是在不同維度上相輔相成。

本文將釐清這些概念的定義和關係,幫助你建立完整的架構知識框架,理解何時該關注架構風格、何時該關注架構模式,以及如何組合使用它們。

文章重點預覽

  • 概念釐清:架構風格 vs 架構模式的本質差異
  • 架構風格:單體、單體模組化、微服務的特點與演進
  • 架構模式:三層架構、MVC/MVP/MVVM、Clean Architecture、Hexagonal Architecture、DDD 的核心概念
  • 關係梳理:架構風格與架構模式如何搭配使用
  • 實務建議:不同場景下的架構選擇策略

閱讀建議:建議先閱讀本文建立整體架構觀念,再深入研究特定的架構風格或模式。文章會用清晰的圖表和類比幫助理解抽象概念。

架構風格 vs 架構模式:核心差異

什麼是架構風格?

架構風格(Architecture Style)關注的是系統的整體組織和部署方式,回答「我的系統由幾個獨立運行的部分組成?它們如何部署和通訊?」這類問題。架構風格決定了系統的物理邊界、部署單位、擴展方式和維運複雜度。

常見的架構風格包含:單體架構(Monolithic)、單體模組化架構(Modular Monolith)、微服務架構(Microservices)、無伺服器架構(Serverless)、事件驅動架構(Event-Driven Architecture)。

架構風格的選擇影響的是非功能需求,例如可擴展性(能否獨立擴展某個模組)、可維護性(團隊能否獨立開發部署)、可靠性(部分失效是否影響全局)、成本(基礎設施和維運成本)。

什麼是架構模式?

架構模式(Architecture Pattern)關注的是程式碼的組織和分層方式,回答「我的程式碼如何組織?各層職責是什麼?依賴關係如何管理?」這類問題。架構模式決定了程式碼的邏輯邊界、模組職責、依賴方向和可測試性。

常見的架構模式包含:三層架構(3-Tier Architecture)、MVC/MVP/MVVMClean ArchitectureHexagonal Architecture(六角架構)、Onion Architecture(洋蔥架構)、DDD(領域驅動設計)的分層架構。

架構模式的選擇影響的是開發效率和程式碼品質,例如可讀性(新人能否快速理解程式碼結構)、可測試性(能否輕鬆撰寫單元測試)、可維護性(修改功能時的影響範圍)、可擴展性(新增功能的難易度)。

兩者的關係與搭配

架構風格和架構模式是正交的(Orthogonal),意思是它們在不同維度上運作,可以自由組合。你可以在單體架構中使用 Clean Architecture,也可以在微服務架構中使用 MVC。關鍵是理解它們解決的問題不同,需要分別考量。

graph TD
    A[軟體架構決策] --> B[架構風格<br/>系統部署與組織]
    A --> C[架構模式<br/>程式碼組織與分層]
    
    B --> B1[單體架構]
    B --> B2[微服務架構]
    B --> B3[Serverless]
    
    C --> C1[MVC/MVP/MVVM]
    C --> C2[Clean Architecture]
    C --> C3[DDD]
    
    B1 -.可搭配.- C1
    B1 -.可搭配.- C2
    B2 -.可搭配.- C2
    B2 -.可搭配.- C3
    
    style A fill:#ff6b6b
    style B fill:#feca57
    style C fill:#48dbfb

一個實際的例子:你可以建構一個「採用微服務架構風格的電商系統」,其中「訂單服務」使用 DDD 架構模式(因為業務邏輯複雜),「通知服務」使用簡單的 三層架構(因為邏輯單純),「用戶服務」使用 Clean Architecture(為了高可測試性)。架構風格決定它們是獨立部署的服務,架構模式決定每個服務內部的程式碼組織。

架構風格詳解

單體架構(Monolithic Architecture)

單體架構是最傳統的架構風格,將整個應用程式打包成單一部署單元。所有功能模組(用戶管理、訂單處理、支付、通知等)都在同一個程序中運行,共享相同的記憶體空間和資料庫連線。

單體架構的特點

優勢方面,單體架構的開發簡單直觀,不需要處理分散式系統的複雜性。部署簡單,只需要部署一個應用程式,不用協調多個服務的版本。除錯容易,所有程式碼在同一個程序中,可以直接追蹤完整的呼叫鏈。效能較好,模組間的呼叫是記憶體內的方法調用,不需要網路通訊的開銷。

劣勢則包含擴展性受限。即使只有某個模組(如圖片處理)需要更多資源,也必須擴展整個應用程式,浪費資源。技術棧綁定,整個應用必須使用相同的程式語言和框架,無法針對不同模組選擇最適合的技術。部署風險高,任何小修改都需要重新部署整個應用,一個模組的 bug 可能導致整個系統崩潰。團隊協作困難,多個團隊修改同一個程式碼庫容易產生衝突,需要頻繁的溝通協調。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 單體架構範例:所有模組在同一個應用中
package main

import (
"myapp/handlers" // HTTP 處理器
"myapp/services" // 業務邏輯
"myapp/repository" // 資料存取
)

func main() {
// 初始化所有模組
db := database.Connect()

userRepo := repository.NewUserRepository(db)
orderRepo := repository.NewOrderRepository(db)
paymentRepo := repository.NewPaymentRepository(db)

userService := services.NewUserService(userRepo)
orderService := services.NewOrderService(orderRepo, paymentRepo)
paymentService := services.NewPaymentService(paymentRepo)

// 所有功能在同一個 HTTP 伺服器中
router := setupRouter()
router.Handle("/users", handlers.NewUserHandler(userService))
router.Handle("/orders", handlers.NewOrderHandler(orderService))
router.Handle("/payments", handlers.NewPaymentHandler(paymentService))

// 單一部署單元
server.Start(":8080", router)
}

單體架構的適用場景

單體架構適合小型到中型專案,特別是團隊規模較小(5-10 人以內)、業務邏輯尚未完全明確、需要快速迭代驗證商業模式的階段。例如新創公司的 MVP(Minimum Viable Product)、內部管理系統、中小型電商平台。

許多成功的大型系統在初期都是單體架構,隨著規模增長才逐步演進為微服務。過早採用微服務反而會增加不必要的複雜度,拖慢開發速度。

單體模組化架構(Modular Monolith)

單體模組化架構是單體和微服務之間的折衷方案。它保持單一部署單元的優點,但在程式碼層面進行嚴格的模組化,每個模組有明確的邊界和介面,模組間的依賴受到嚴格控制。

模組化單體的設計原則

模組邊界清晰是核心原則。每個模組應該有自己的資料庫 Schema(邏輯上的分離,可能是同一個實體資料庫的不同 Schema),其他模組不能直接存取其資料表。模組間通過明確的 API 介面互動,不能直接調用內部實現。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 模組化單體範例:嚴格的模組邊界

// 用戶模組:對外暴露介面
package user

type UserModule interface {
GetUser(id string) (*User, error)
CreateUser(req CreateUserRequest) (*User, error)
ValidateUser(id string) bool // 供其他模組調用
}

// 內部實現對外不可見
type userModuleImpl struct {
repo UserRepository // 只存取自己的資料表
}

// 訂單模組:只能通過介面使用用戶模組
package order

type OrderService struct {
orderRepo OrderRepository
userModule user.UserModule // 依賴介面而非實現
}

func (s *OrderService) CreateOrder(req CreateOrderRequest) (*Order, error) {
// 通過介面驗證用戶,不直接存取 users 資料表
if !s.userModule.ValidateUser(req.UserID) {
return nil, errors.New("invalid user")
}

order := &Order{
UserID: req.UserID,
Items: req.Items,
}
return s.orderRepo.Save(order)
}

// 外部不能直接修改 items,必須通過方法

模組化單體的優勢

相比傳統單體,模組化單體保留了開發和部署的簡單性,不需要處理分散式系統的複雜性(如服務發現、分散式追蹤、最終一致性)。同時,它提供了更好的可維護性,清晰的模組邊界讓程式碼更容易理解和修改。

更重要的是,模組化單體為未來的微服務遷移鋪路。當某個模組成為瓶頸或需要獨立擴展時,可以將該模組抽離成獨立的微服務,因為模組邊界已經清晰定義,遷移成本較低。這種演進式架構避免了過早微服務化的風險。

微服務架構(Microservices Architecture)

微服務架構將應用程式拆分為多個小型、獨立部署的服務,每個服務負責特定的業務能力,擁有自己的資料庫,通過輕量級的通訊協議(如 REST API、gRPC、訊息佇列)互動。

微服務的核心特徵

服務自治是微服務的基本原則。每個服務可以獨立開發、部署、擴展和升級,不需要等待其他服務。服務擁有自己的資料庫(Database per Service),避免資料庫層面的耦合。服務可以選擇最適合自己的技術棧,例如用戶服務用 Java、訂單服務用 Go、推薦服務用 Python。

去中心化治理意味著沒有統一的技術標準,各團隊可以自主決策。這帶來靈活性,但也需要更成熟的工程文化和治理機制。

graph TB
    A[API Gateway] --> B[用戶服務<br/>Node.js + MongoDB]
    A --> C[訂單服務<br/>Go + PostgreSQL]
    A --> D[支付服務<br/>Java + MySQL]
    A --> E[通知服務<br/>Python + Redis]
    
    C --> F[訊息佇列<br/>RabbitMQ]
    D --> F
    F --> E
    
    B --> G[(用戶資料庫)]
    C --> H[(訂單資料庫)]
    D --> I[(支付資料庫)]
    
    style A fill:#ff6b6b
    style B fill:#feca57
    style C fill:#48dbfb
    style D fill:#1dd1a1
    style E fill:#ff9ff3

微服務的挑戰

微服務帶來的複雜度不容忽視。分散式系統的經典問題都會出現:網路延遲和失敗、資料一致性、分散式交易、服務間的循環依賴。維運複雜度大幅增加,需要服務發現、負載平衡、健康檢查、分散式追蹤、集中式日誌等基礎設施。

資料一致性是最大的挑戰之一。由於每個服務有自己的資料庫,無法使用傳統的 ACID 交易保證一致性,必須採用最終一致性(Eventual Consistency)和 Saga 模式來處理跨服務的業務流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 微服務架構範例:服務間的非同步通訊

// 訂單服務:處理訂單建立
func (s *OrderService) CreateOrder(req CreateOrderRequest) error {
// 1. 建立訂單(本地交易)
order := &Order{
ID: generateID(),
UserID: req.UserID,
Items: req.Items,
Status: StatusPending,
}
if err := s.orderRepo.Save(order); err != nil {
return err
}

// 2. 發布事件到訊息佇列(非同步)
event := OrderCreatedEvent{
OrderID: order.ID,
TotalAmount: order.TotalAmount,
UserID: order.UserID,
}
s.eventPublisher.Publish("order.created", event)

// 不等待支付和通知完成就返回
return nil
}

// 支付服務:監聽訂單事件
func (s *PaymentService) HandleOrderCreated(event OrderCreatedEvent) {
// 處理支付邏輯
payment := s.processPayment(event.OrderID, event.TotalAmount)

// 發布支付結果事件
if payment.Success {
s.eventPublisher.Publish("payment.completed", payment)
} else {
s.eventPublisher.Publish("payment.failed", payment)
}
}

微服務的適用場景

微服務適合大型複雜系統,特別是有以下特徵的場景:團隊規模大(超過 20-30 人),需要多個團隊並行開發;業務領域複雜,可以清晰劃分為多個子領域;不同模組有不同的擴展需求,例如支付模組需要高可用、推薦模組需要高運算能力;技術異構需求,不同模組適合不同的技術棧。

典型的例子包含大型電商平台(淘寶、Amazon)、社交媒體平台(Facebook、Twitter)、串流媒體服務(Netflix)。這些系統的共同點是業務極其複雜、流量巨大、需要高度的可擴展性和可用性。

架構模式詳解

三層架構(3-Tier Architecture)

三層架構是最基礎的架構模式,將應用程式分為表現層(Presentation Layer)、業務邏輯層(Business Logic Layer)、資料存取層(Data Access Layer)三個邏輯層。

三層架構的組成

表現層負責與使用者互動,接收輸入、顯示輸出。在 Web 應用中是 HTTP 路由和 Handler,在桌面應用中是 UI 介面。表現層應該保持輕量,只處理資料格式轉換和驗證,不包含業務邏輯。

業務邏輯層是系統的核心,包含所有的業務規則、計算邏輯、流程控制。它獨立於表現層和資料存取層,確保業務邏輯可以被不同的 UI 或資料源重複使用。

資料存取層封裝所有與資料庫的互動,包含 SQL 查詢、ORM 操作、快取處理。上層不應該知道資料的儲存細節,通過介面與資料存取層互動。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 三層架構範例

// 表現層:HTTP Handler
type UserHandler struct {
userService *services.UserService
}

func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
var req CreateUserRequest
json.NewDecoder(r.Body).Decode(&req)

user, err := h.userService.CreateUser(req)
if err != nil {
respondError(w, err)
return
}
respondJSON(w, user)
}

// 業務邏輯層:Service
type UserService struct {
userRepo repository.UserRepository
}

func (s *UserService) CreateUser(req CreateUserRequest) (*User, error) {
if len(req.Username) < 3 {
return nil, errors.New("username too short")
}

existing, _ := s.userRepo.FindByUsername(req.Username)
if existing != nil {
return nil, errors.New("username exists")
}

user := &User{Username: req.Username, Email: req.Email}
return s.userRepo.Create(user)
}

// 資料存取層:Repository
type UserRepository interface {
Create(user *User) (*User, error)
FindByUsername(username string) (*User, error)
}

三層架構的優點是概念簡單,容易理解和實作,適合大部分中小型專案。缺點是容易演變成「貧血模型」(Anemic Domain Model),業務邏輯散落在 Service 層,資料模型只是單純的資料容器,缺乏行為封裝。

MVC / MVP / MVVM

這三個模式都是三層架構的變體,主要用於前端或有 UI 的應用程式,關注如何分離 UI 邏輯和業務邏輯。

MVC(Model-View-Controller)將應用分為 Model(資料和業務邏輯)、View(UI 顯示)、Controller(處理使用者輸入)。這是最經典的模式,被大量 Web 框架採用。

MVP(Model-View-Presenter)改進了 MVC 的 View-Model 耦合問題。Presenter 完全接管了 View 的邏輯,View 變成被動元件,極大提升可測試性。

MVVM(Model-View-ViewModel)引入了資料綁定機制,ViewModel 和 View 通過雙向綁定自動同步。這是現代前端框架(Vue、React、Angular)的核心模式,大幅簡化了 UI 更新邏輯。

Clean Architecture(整潔架構)

Clean Architecture 強調業務邏輯的獨立性。核心思想是將系統分為多個同心圓層,內層不依賴外層,確保業務邏輯不受框架、資料庫、UI 的影響。

依賴規則:依賴方向只能由外向內。外層可以依賴內層,但內層絕不能知道外層的存在。

四層結構Entities 層(企業級業務規則)、Use Cases 層(應用程式特定的業務規則)、Interface Adapters 層(轉換資料格式)、Frameworks & Drivers 層(外部工具和框架)。

Clean Architecture 的優勢在於高度可測試技術無關。要更換資料庫或 Web 框架,只需要替換最外層的實現,業務邏輯完全不受影響。

Hexagonal Architecture(六角架構)

Hexagonal Architecture(Ports and Adapters)與 Clean Architecture 理念相近。核心理念是將應用程式核心與外部系統隔離,通過(Ports)進行互動。

Primary Ports(主埠)是應用程式提供的服務介面,由外部調用。Secondary Ports(次埠)是應用程式需要的外部服務介面,由應用程式調用。

Adapters(適配器)是介面的具體實現。應用程式核心不知道是誰在調用它,也不知道資料儲存在哪裡,使系統高度可移植可測試

Onion Architecture(洋蔥架構)

Onion Architecture 與 Clean Architecture 非常相似,都強調依賴反轉和分層。主要差異在於視覺化表達:用洋蔥的層次比喻,強調從核心向外的層層包裹。

Onion Architecture 常與 DDD 搭配使用,因為它的層次劃分非常適合 DDD 的戰術設計模式。

DDD(Domain-Driven Design)

DDD 不僅是架構模式,更是一套完整的設計方法論。它強調與領域專家緊密合作,用通用語言建立領域模型,將複雜的業務邏輯轉化為清晰的程式碼結構。

戰略設計Bounded Context(限界上下文)將大型系統劃分為多個邊界明確的子領域,Context Map 定義不同上下文之間的關係。

戰術設計Entity(實體)具有唯一識別,Value Object(值物件)由屬性定義,Aggregate(聚合)是一組物件的集合由聚合根統一管理,Domain Service 處理不屬於任何實體的業務邏輯,Repository 提供集合式的介面存取聚合。

DDD 適用於業務邏輯複雜且變化頻繁的系統,但學習曲線陡峭,不適合簡單的 CRUD 應用。

架構風格與架構模式的關聯

常見的搭配組合

單體架構 + MVC:最經典的組合,適合小型專案快速開發。

單體架構 + Clean Architecture:適合中型專案,需要良好的可測試性和可維護性。

模組化單體 + DDD:適合複雜業務但尚未大到需要微服務的系統。

微服務 + Hexagonal Architecture:適合大型分散式系統,每個服務內部使用 Hexagonal Architecture。

微服務 + DDD:最強大但也最複雜的組合,適合超大型複雜系統。

演進路徑

實務中,架構通常是演進而非一次到位的。

階段一:單體 + MVC → 快速驗證商業模式
階段二:單體 + 模組化 → 建立清晰的模組邊界
階段三:模組化單體 + Clean Architecture / DDD → 提高可測試性
階段四:微服務 + DDD → 將熱點模組拆分為微服務

graph LR
    A[階段一<br/>單體 + MVC] --> B[階段二<br/>模組化單體]
    B --> C[階段三<br/>+ Clean Arch/DDD]
    C --> D[階段四<br/>部分微服務]
    D --> E[階段五<br/>全面微服務]
    
    style A fill:#feca57
    style B fill:#48dbfb
    style C fill:#1dd1a1
    style D fill:#ff9ff3
    style E fill:#ff6b6b

關鍵是不要過早優化。保持程式碼的可重構性,讓架構能夠隨著業務演進。

總結

架構決策不是非黑即白,而是在多個維度上的權衡。簡單性應該優先考慮,只有在真正需要時才引入複雜性。演進式思維是關鍵,從簡單開始,保持程式碼的可重構性,讓架構能夠隨著需求演進。

記住,架構是為業務服務的工具,不是目的本身。選擇適合團隊和專案的架構,持續學習和改進,才能在不同場景下做出明智的架構決策。


延伸閱讀

  • 📘 《Building Microservices》by Sam Newman
  • 📗 《Clean Architecture》by Robert C. Martin
  • 📙 《Domain-Driven Design》by Eric Evans
  • 🔗 Martin Fowler’s Architecture Guide

系列文章

  • 軟體架構模式全解析:從 MVC 到 DDD 的演進與實踐
  • 系統設計系列(二):微服務架構模式與實踐(敬請期待)

如果這篇文章對你有幫助,歡迎分享給更多朋友!