为什么不用微服务?
Art Admin 采用模块化单体架构,而非微服务。本文解释这个决策背后的考量,以及如果你确实需要微服务时该怎么做。
单体 ≠ 混乱
很多人对"单体"有误解,认为单体就是代码堆在一起、无法维护。但 Art Admin 的单体是模块化的:
- 四层清洁架构(
Api → Core → Domain ← Infra),职责清晰 - 业务按模块划分在
Art.Core/Services/下,天然隔离 - 多端 API 隔离(Admin / App / Common),互不干扰
为什么不用微服务?
1. 快速开发优先
本框架的设计目标是快速构建应用。在业务爆发前,单体架构能显著降低开发和运维复杂度:
| 维度 | 单体 | 微服务 |
|---|---|---|
| 开发调试 | 一个进程,F5 启动 | 多个服务,需要编排 |
| 数据一致性 | 本地事务 | 分布式事务(Saga / TCC) |
| 部署运维 | 一个容器 | N 个容器 + 服务发现 + 网关 |
| 团队要求 | 1-5 人即可 | 通常需要 DevOps 团队 |
2. 多端鉴权已内置
已支持管理端、应用端、公共端三套独立 API 和认证体系,满足绝大多数业务场景。不需要拆服务来实现多端隔离。
3. 清洁架构易拆分
四层分离设计使得未来拆分微服务成本极低:
Art.Core/Services/
├── Admin/
│ ├── UserService.cs → 拆成 User 微服务
│ ├── OrderService.cs → 拆成 Order 微服务
│ └── ProductService.cs → 拆成 Product 微服务
└── App/
└── AppOrderService.cs → 合并到 Order 微服务只需按 Domain 边界拆分,每个服务独立部署,共享 Domain 和 Infra 层即可。
4. 避免过早优化
在业务验证阶段,微服务带来的问题往往大于收益:
- 网络延迟 — 服务间调用增加 1-10ms 延迟
- 分布式事务 — 实现 Saga 模式代价高昂
- 运维成本 — 服务发现、链路追踪、日志聚合、配置中心
- 调试困难 — 问题可能横跨多个服务,排查复杂
💡 过早引入微服务是最常见的架构错误之一。先用单体快速验证业务,等流量和团队规模真正需要时再拆分。
如果确实需要微服务
当你的业务规模确实需要微服务时,Art Admin 的清洁架构能让你低成本迁移。以下是推荐方案:
API 网关选择
| 网关 | 推荐度 | 说明 |
|---|---|---|
| YARP | ⭐⭐⭐⭐⭐ | 微软官方出品,轻量高性能,.NET 原生,配置简单 |
| Ocelot | ⭐⭐⭐⭐ | .NET 生态成熟方案,功能丰富,社区活跃 |
| Nginx | ⭐⭐⭐ | 通用方案,需要额外维护配置 |
| Kong / APISIX | ⭐⭐ | 功能强大但引入额外技术栈 |
推荐 YARP
如果追求轻量和高性能,首选 YARP(Yet Another Reverse Proxy)。它是微软官方维护的 .NET 反向代理库,直接集成到 ASP.NET Core 中,无需额外进程。
csharp
// YARP 网关配置示例
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
app.MapReverseProxy();json
{
"ReverseProxy": {
"Routes": {
"user-route": {
"ClusterId": "user-cluster",
"Match": { "Path": "/api/user/{**catch-all}" }
},
"order-route": {
"ClusterId": "order-cluster",
"Match": { "Path": "/api/order/{**catch-all}" }
}
},
"Clusters": {
"user-cluster": {
"Destinations": {
"destination1": { "Address": "http://user-service:8080" }
}
},
"order-cluster": {
"Destinations": {
"destination1": { "Address": "http://order-service:8080" }
}
}
}
}
}如果需要更丰富的功能(限流、熔断、认证等开箱即用),可以选择 Ocelot:
json
{
"Routes": [
{
"DownstreamPathTemplate": "/api/user/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{ "Host": "user-service", "Port": 8080 }
],
"UpstreamPathTemplate": "/api/user/{everything}",
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ]
}
]
}拆分步骤
- 识别边界 — 按
Art.Core/Services/下的模块划分服务边界 - 独立数据库 — 每个服务拥有自己的数据库 Schema
- 引入网关 — 使用 YARP 或 Ocelot 统一入口
- 异步通信 — 服务间用消息队列解耦(已有 Redis MQ 基础设施)
- 逐步迁移 — 一次拆一个模块,而非一次性重构
提醒
拆分微服务是一个渐进过程,不要试图一步到位。先拆分流量最大或变更最频繁的模块,其余保持单体。