输入关键词开始搜索

分层架构设计 — 模式、逻辑与选型

经典三层(使用最广)

┌──────────────────────────┐
│      表示层 (UI)          │  ← Controller / View
├──────────────────────────┤
│      业务逻辑层           │  ← Service / UseCase
├──────────────────────────┤
│      数据访问层           │  ← Repository / DAO
└──────────────────────────┘
依赖方向:表示层 → 业务层 → 数据层
业务层不依赖具体 UI 或数据库实现
// 三层示例 — C++
class UserRepository {  // 数据层
public:
    virtual User findById(int id) = 0;
};
class SqlUserRepo : public UserRepository {
    User findById(int id) override { /* SELECT */ }
};

class UserService {     // 业务层(依赖接口,不依赖实现)
    UserRepository &repo;
public:
    UserService(UserRepository &r) : repo(r) {}
    User getProfile(int id) { return repo.findById(id); }
};

class UserController {  // 表示层
    UserService &svc;
public:
    Json handleRequest(int id) {
        auto user = svc.getProfile(id);
        return user.toJson();
    }
};

三层选择考量

考量三层
简单项目(<10 个接口)✅ 刚好
业务逻辑复杂、多变⚠️ Service 容易膨胀
多端 UI(Web/桌面/移动)✅ 复用业务层
需要替换数据库✅ Repository 接口隔离

DDD(领域驱动设计)

┌──────────────────────────────┐
│        接口层 (API/UI)       │
├──────────────────────────────┤
│        应用层 (UseCase)      │  ← 编排领域对象
├──────────────────────────────┤
│    领域层 (Entity/ValueObj)  │  ← 核心业务规则
│    ┌──────────┬──────────┐   │
│    │ 聚合根   │ 领域服务  │   │
│    │ 值对象   │ 领域事件  │   │
│    └──────────┴──────────┘   │
├──────────────────────────────┤
│    基础设施层 (DB/MQ/Cache)   │
└──────────────────────────────┘
依赖方向:全部指向领域层(领域层不依赖任何外部)
// DDD 示例 — 订单领域
class Money {  // 值对象(无 ID,不可变)
    double amount;
    string currency;
public:
    Money add(const Money &other) const {
        if (currency != other.currency) throw ...;
        return {amount + other.amount, currency};
    }
};

class Order {  // 实体 / 聚合根
    int id;           // 实体的标识
    Money total;
    OrderStatus status;
public:
    void addItem(const Item &item) {
        total = total.add(item.price);  // 业务规则在领域内
    }
    void submit() {
        if (status != OrderStatus::Draft) throw ...;
        status = OrderStatus::Submitted;
    }
};

DDD 选择考量

考量DDD
业务规则复杂多变✅ 业务规则归位到领域对象
团队懂业务语言✅ 统一语言(Ubiquitous Language)
简单 CRUD 项目❌ 过度设计
团队大、需要分模块✅ 限界上下文(Bounded Context)

六边形架构(端口-适配器)

          ┌──────────────────┐
   Web ──→│                  │──→ DB
          │   业务核心        │
  REST ──→│  (无外部依赖)     │──→ MQ
          │                  │
  CLI  ──→│                  │──→ Cache
          └──────────────────┘
   ← 主端口 (输入)   次端口 (输出) →
// 端口 = 接口
class OrderRepository {  // 次端口(输出端口)
public:
    virtual void save(const Order &order) = 0;
};

class OrderUseCase {     // 业务核心
    OrderRepository &repo;
public:
    void submitOrder(Order &order) {
        order.submit();
        repo.save(order);
    }
};

// 适配器 = 接口的具体实现
class PostgresOrderRepo : public OrderRepository { /* ... */ };
class MongoOrderRepo : public OrderRepository { /* ... */ };

六边形选择考量

考量六边形
需要替换基础设施✅ 换数据库只需新适配器
多输入渠道✅ Web / CLI / 消息队列统一
测试驱动✅ 所有依赖可 mock
小项目❌ 接口数量爆炸

CQRS(读写分离)

         ┌─────────────┐
  写请求 →   Command    → 写 Model → DB (主)
         └─────────────┘

         ┌─────────────┐
  读请求 →   Query      → 读 Model → DB (从) / ES
         └─────────────┘
// 命令侧 — 只管写
class CreateOrderCommand {
    int userId;
    vector<Item> items;
};

class CreateOrderHandler {
    void handle(const CreateOrderCommand &cmd) {
        Order order = Order::create(cmd.userId, cmd.items);
        orderRepo.save(order);
        eventBus.publish(OrderCreatedEvent{order.id()});
    }
};

// 查询侧 — 只管读(可走缓存/只读副本/ES)
class OrderQueryService {
    OrderDTO findById(int id) {
        // 可能直接查 ES / Redis / 只读从库
        return readDb.query("SELECT ...").mapTo<OrderDTO>();
    }
};

CQRS 选择考量

考量CQRS
读 >> 写(报表/仪表盘)✅ 读模型专门优化
读写模型差异大✅ 互不干扰
简单 CRUD❌ 双倍模型,过度设计
团队小❌ 维护成本高

分层决策树

业务逻辑复杂 + 领域专家参与?
  ├─ 是 → DDD(四层 + 限界上下文)
  └─ 否 → CRUD 为主?
            ├─ 是 → 经典三层
            └─ 否 → 需要频繁替换基础设施?
                      ├─ 是 → 六边形架构
                      └─ 否 → 读写压力差异大?
                                ├─ 是 → CQRS
                                └─ 否 → 三层(够了)

层间依赖规则(所有架构适用)

1. 上层依赖下层,下层不依赖上层(依赖倒置)
2. 层间通信尽量通过接口(类型擦除)
3. 同一层的组件可以互相引用
4. 禁止跨层调用(表示层绝对不能直接调 DAO)
5. 数据对象不跨层「裸奔」— DTO/Entity 转换在层边界