Go后端架構探索: MVC 與 DDD 分層架構有何不同?
Go語言 MVC 與 DDD 分層架構詳細對比
MVC和DDD是后臺開發兩種流行的分層架構思想,MVC(Model-View-Controller)是一種設計模式,主要用于分離用戶界面、業務邏輯和數據模型,便于分層解耦,而DDD(領域驅動設計)則是一種架構方法論,旨在通過構建業務領域模型來解決復雜系統中的設計和維護難題。
在Java領域,很多系統逐漸由MVC逐漸轉為DDD,但在Go、Python、NodeJS等語言,秉持簡單高效的理念,MVC依然是主流架構。下面基于Go語言來具體探討下MVC和DDD兩種目錄結構的區別。

MVC 圖形結構
+------------------+
| View | 用戶界面層:負責數據展示和用戶交互(如HTML頁面、API響應)
+------------------+
| Controller | 控制層:處理用戶請求,調用Service邏輯,協調Model與View
+------------------+
| Model | 模型層:包含數據對象(如數據庫表結構)和部分業務邏輯(常分散在Service層)
+------------------+
DDD 圖形結構
+--------------------+
| 用戶界面層(UI) | 負責用戶交互和展示(如REST API、Web界面)
+--------------------+
| 應用層(Application)| 編排業務流程(如調用領域服務、事務管理),不包含核心業務規則
+--------------------+
| 領域層(Domain) | 核心業務邏輯層:包含聚合根、實體、值對象、領域服務等,內聚業務規則
+--------------------+
| 基礎設施層 | 提供技術實現(如數據庫訪問、消息隊列、外部API)
| (Infrastructure) |
+--------------------+
MVC 與 DDD 的主要區別:
1. 代碼組織邏輯
MVC 按技術功能分層(Controller/Service/DAO),關注技術實現;DDD 按業務領域劃分模塊(如訂單域、支付域),以限界上下文隔離核心業務邏輯。
2. 業務邏輯載體
MVC 通常采用貧血模型,數據(Model)與行為(Service)分離,邏輯分散導致維護成本高;DDD 通過聚合根、領域服務實現充血模型,業務邏輯內聚于領域層,增強可擴展性。
3. 適用性與成本
MVC 開發成本低,適合需求穩定的中小型系統;DDD 需前期領域建模和統一語言,適用于業務復雜、需長期演進的大型系統,但團隊需具備領域抽象能力。例如,電商促銷規則用 DDD 可避免邏輯散落在多個 Service 中。
Go語言 MVC 目錄結構
MVC主要分為三層:視圖、控制器、模型。
gin-order/
├── cmd
│ └── main.go # 應用入口,啟動 Gin 引擎
├── internal
│ ├── controllers # 控制器層(處理 HTTP 請求),也可以叫handlers
│ │ └── order
│ │ └── order_controller.go # Order 模塊的控制器
│ ├── services # 服務層(業務邏輯處理)
│ │ └── order
│ │ └── order_service.go # Order 模塊的服務實現
│ ├── repository # 數據訪問層(與數據庫交互)
│ │ └── order
│ │ └── order_repository.go # Order 模塊的數據訪問接口及實現
│ ├── models # 模型層(數據結構定義)
│ │ └── order
│ │ └── order.go # Order 模塊的數據模型
│ ├── middleware # 中間件(如鑒權、日志、請求攔截)
│ │ ├── logging.go # 日志中間件
│ │ └── auth.go # 鑒權中間件
│ └── config # 配置模塊(數據庫、服務器等配置)
│ └── config.go # 應用與環境配置
├── pkg # 公共工具包(如響應包裝工具)
│ └── response.go # 響應處理工具方法
├── web # 前端資源(模板與靜態資源)
│ ├── static # 靜態資源(CSS、JS、圖片)
│ └── templates # 模板文件(HTML模板)
│ └── order.tmpl # Order 模塊的視圖模板(如果需要渲染HTML)
├── go.mod # Go 模塊管理文件
└── go.sum # Go 模塊依賴版本鎖定
Go語言 DDD 目錄結構
DD主要分為四層:界面、應用、領域、基礎。
go-web/
│── cmd/
│ └── main.go # 應用入口
│── internal/
│ ├── application/ # 應用層(協調領域邏輯,處理業務用例)
│ │ ├── services/ # 服務層,業務邏輯目錄
│ │ │ └── order_service.go # 訂單應用服務,調用領域層業務邏輯
│ ├── domain/ # 領域層(核心業務邏輯和接口定義)
│ │ ├── order/ # 訂單聚合
│ │ │ ├── order.go # 訂單實體(聚合根),包含核心業務邏輯
│ │ ├── repository/ # 通用倉庫接口
│ │ │ ├── repository.go # 通用倉庫接口(通用 CRUD 操作)
│ │ │ └── order_repository.go # 訂單倉儲接口,定義對訂單數據的操作
│ ├── infrastructure/ # 基礎設施層(實現領域層定義的接口)
│ │ ├── repository/ # 倉儲實現
│ │ │ └── order_repository_impl.go # 訂單倉儲實現,具體的訂單數據存儲
│ └── interfaces/ # 接口層(處理外部請求,如HTTP接口)
│ │ ├── handlers/ # HTTP 處理器
│ │ │ └── order_handler.go # 訂單相關的HTTP處理器
│ │ └── routes/
│ │ │ ├── router.go # 基礎路由工具設置
│ │ │ └── order-routes.go # 訂單路由地址配置
│ │ │ └── order-routes-test.go # 訂單路由測試
│ └── middleware/ # 中間件(例如:鑒權、攔截、認證等)
│ │ └── logging.go # 日志中間件
│ ├── config/ # 服務相關配置
│ │ └── server_config.go # 服務器配置(如端口、超時設置等)
│── pkg/ # 可復用的公共庫
│ └── utils/ # 工具類(例如:日志、日期處理等)
Go語言 MVC 代碼實現
源碼:https://github.com/microwind/design-patterns/tree/main/mvx/mvc/gin-mvc
Controller(接口層) → Service(業務邏輯層) → Repository(數據訪問層) → Model(數據模型)
分層代碼
- 控制器層(Controller)
// internal/controller/order/order.go
package order
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/gin-order/internal/model"
"github.com/gin-order/internal/service/order"
"github.com/gin-order/internal/pkg/response"
)
type OrderController struct {
service *order.OrderService
}
func NewOrderController(service *order.OrderService) *OrderController {
return &OrderController{service: service}
}
func (c *OrderController) GetOrder(ctx *gin.Context) {
idStr := ctx.Param("id")
id, _ := strconv.ParseUint(idStr, 10, 64)
order, err := c.service.GetOrderByID(uint(id))
if err != nil {
response.Error(ctx, http.StatusNotFound, "Order not found")
return
}
response.Success(ctx, order)
}
func (c *OrderController) CreateOrder(ctx *gin.Context) {
var req model.Order
if err := ctx.ShouldBindJSON(&req); err != nil {
response.Error(ctx, http.StatusBadRequest, "Invalid request")
return
}
if err := c.service.CreateOrder(&req); err != nil {
response.Error(ctx, http.StatusInternalServerError, "Create failed")
return
}
response.Success(ctx, req)
}
- 路由配置
// cmd/server/main.go
package main
import (
"github.com/gin-gonic/gin"
"github.com/gin-order/internal/controller/order"
"github.com/gin-order/internal/pkg/database"
"github.com/gin-order/internal/repository/order"
"github.com/gin-order/internal/service/order"
)
func main() {
// 初始化數據庫
db := database.NewGORM()
// 依賴注入
orderRepo := order_repo.NewMySQLOrderRepository(db)
orderService := order_service.NewOrderService(orderRepo)
orderController := order_controller.NewOrderController(orderService)
// 創建路由
r := gin.Default()
// 注冊中間件
r.Use(middleware.Logger())
// 路由分組
apiGroup := r.Group("/api")
{
orderGroup := apiGroup.Group("/orders")
{
orderGroup.GET("/:id", orderController.GetOrder)
orderGroup.POST("", orderController.CreateOrder)
}
}
// 啟動服務
r.Run(":8080")
}
- 服務層(Service)
// internal/service/order/service.go
package order
import (
"github.com/gin-order/internal/model"
"github.com/gin-order/internal/repository/order"
)
type OrderService struct {
repo order.OrderRepository
}
func NewOrderService(repo order.OrderRepository) *OrderService {
return &OrderService{repo: repo}
}
func (s *OrderService) GetOrderByID(id uint) (*model.Order, error) {
return s.repo.FindByID(id)
}
func (s *OrderService) CreateOrder(order *model.Order) error {
return s.repo.Create(order)
}
- 數據訪問層(Repository)
// internal/repository/order/interface.go
package order
import "github.com/gin-order/internal/model"
type OrderRepository interface {
FindByID(id uint) (*model.Order, error)
Create(order *model.Order) error
FindByStatus(status string) ([]model.Order, error)
}
// internal/repository/order/mysql.go
package order
import (
"gorm.io/gorm"
"github.com/gin-order/internal/model"
)
type MySQLOrderRepository struct {
db *gorm.DB
}
func NewMySQLOrderRepository(db *gorm.DB) OrderRepository {
return &MySQLOrderRepository{db: db}
}
func (r *MySQLOrderRepository) FindByID(id uint) (*model.Order, error) {
var order model.Order
if err := r.db.First(&order, id).Error; err != nil {
return nil, err
}
return &order, nil
}
func (r *MySQLOrderRepository) Create(order *model.Order) error {
return r.db.Create(order).Error
}
func (r *MySQLOrderRepository) FindByStatus(status string) ([]model.Order, error) {
var orders []model.Order
if err := r.db.Where("status = ?", status).Find(&orders).Error; err != nil {
return nil, err
}
return orders, nil
}
- 模型層(Model)
// internal/model/order.go
package model
import "time"
type Order struct {
OrderID uint `gorm:"primaryKey;column:order_id"`
OrderNo string `gorm:"uniqueIndex;column:order_no"`
UserID uint `gorm:"index;column:user_id"`
OrderName string `gorm:"column:order_name"`
Amount float64 `gorm:"type:decimal(10,2);column:amount"`
Status string `gorm:"column:status"`
CreatedAt time.Time `gorm:"column:created_at"`
UpdatedAt time.Time `gorm:"column:updated_at"`
}
func (Order) TableName() string {
return "orders"
}
Go語言 MVC 最佳實踐
接口隔離原則
Repository 層通過接口定義,支持多種數據庫實現
// 可輕松切換為 Mock 實現
type MockOrderRepository struct {}
func (m *MockOrderRepository) FindByID(id uint) (*model.Order, error) {
return &model.Order{OrderNo: "mock-123"}, nil
}
統一響應格式
// pkg/response/response.go
func Success(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "success",
"data": data,
})
}
中間件鏈
// 全局中間件
r.Use(gin.Logger(), gin.Recovery())
// 路由組中間件
adminGroup := r.Group("/admin", middleware.AuthJWT())
數據庫遷移
使用 GORM AutoMigrate:
db.AutoMigrate(&model.Order{})
Go語言 DDD 代碼實現與最佳實踐
源碼:https://github.com/microwind/design-patterns/tree/main/domain-driven-design/go-web
1. 關注領域模型
DDD 強調領域模型的構建,使用 聚合(Aggregate)、實體(Entity)、值對象(Value Object) 組織業務邏輯。
在 Go 語言中,通常使用 struct 定義實體和值對象:
// 實體(Entity)
type User struct {
ID int
Name string
}
2. 分層架構
DDD 通常采用 分層架構,Go 語言項目可以遵循如下結構:
- 領域層(Domain Layer):核心業務邏輯,如
domain目錄下的實體和聚合。 - 應用層(Application Layer):用例(Use Cases)和業務流程編排。
- 基礎設施層(Infrastructure Layer):數據庫、緩存、外部 API 適配等。
- 接口層(Interface Layer):提供 HTTP、gRPC 或 CLI 接口。
3. 依賴倒置(Dependency Inversion)
領域層不應直接依賴基礎設施,而是通過 接口(Interface) 進行依賴倒置。
注:DDD架構核心就是依賴倒置(DIP),Domain是最核心的內層,僅定義業務規則和接口抽象,其他層級依賴Domain實現,Domain不依賴任何外部實現。在六邊形架構(Hexagonal Architecture)中,領域層位于核心,其他層級(如應用層、基礎設施層)通過實現領域層定義的接口來提供具體技術細節(如數據庫操作、API 調用),從而達成領域與技術實現的解耦。
// 領域層:定義接口
type UserRepository interface {
GetByID(id int) (*User, error)
}
// 基礎設施層:數據庫實現
type userRepositoryImpl struct {
db *sql.DB
}
func (r *userRepositoryImpl) GetByID(id int) (*User, error) {
// 數據庫查詢邏輯
}
4. 聚合(Aggregate)管理
聚合根(Aggregate Root)管理整個聚合的生命周期:
type Order struct {
ID int
Items []OrderItem
Status string
}
func (o *Order) AddItem(item OrderItem) {
o.Items = append(o.Items, item)
}
5. 應用服務(Application Service)
應用服務封裝領域邏輯,避免外部直接操作領域對象:
type OrderService struct {
repo OrderRepository
}
func (s *OrderService) CreateOrder(userID int, items []OrderItem) (*Order, error) {
order := Order{UserID: userID, Items: items, Status: "Pending"}
return s.repo.Save(order)
}
6. 事件驅動(Event-Driven)
使用 領域事件(Domain Events) 進行解耦,在 Go 語言中可通過 Channel 或 Pub/Sub 實現:
type OrderCreatedEvent struct {
OrderID int
}
def publishEvent(event OrderCreatedEvent) {
go func() {
eventChannel <- event
}()
}
7. 結合 CQRS(命令查詢職責分離)
DDD 可結合 CQRS(Command Query Responsibility Segregation),在 Go 語言中可用 命令(Command) 處理變更操作,用 查詢(Query) 處理數據讀取:
type CreateOrderCommand struct {
UserID int
Items []OrderItem
}
func (h *OrderHandler) Handle(cmd CreateOrderCommand) (*Order, error) {
return h.service.CreateOrder(cmd.UserID, cmd.Items)
}
MVC與DDD架構總結
1. 架構核心區別
| 維度 | MVC 架構 | DDD 架構 |
|---|---|---|
| 層級 | 三層:Controller/Service/DAO | 四層:接口層/應用層/領域層/基礎設施層 |
| 職責 | - Controller 處理請求,Service 承載邏輯 - DAO 直接操作數據庫 |
- 應用層編排流程(如調用領域服務) - 領域層內聚業務原子操作(如訂單創建規則) - 基礎設施層實現技術細節(如數據庫訪問) |
| 痛點 | Service 層臃腫,業務邏輯與數據操作耦合 | 領域層獨立于技術實現,邏輯與層級強對應 |
2. 模塊化與擴展性
MVC:
- 高耦合:缺乏明確的業務邊界,跨模塊調用(如訂單服務直接依賴賬戶表)導致代碼難以維護。
- 擴展性差:新增功能需全局修改(如添加風控規則需侵入訂單服務),易引發連鎖問題。
DDD:
- 限界上下文:按業務能力劃分模塊(如支付域、風控域),通過事件驅動(如訂單支付完成事件)解耦協作。
- 獨立演進:各領域模塊可獨立升級(如支付邏輯優化不影響訂單服務),降低系統級風險。
3. 適用場景區別
- 中小系統優先 MVC:業務簡單(如博客、CMS、管理后臺),需快速開發且業務規則清晰,無后續反復變更。
- 復雜業務轉向 DDD:規則密集(如金融交易、供應鏈)、多領域協作(如電商訂單與庫存聯動),后續變更頻繁。

浙公網安備 33010602011771號