微服务与整体架构:做出正确的架构决策
72% 的采用微服务的公司过早地报告了复杂性的增加,却没有带来相应的好处。 微服务的炒作导致许多团队将他们的应用程序分布在数十个服务中,而结构良好的整体应用程序本来可以更好、更快、更便宜地为他们提供服务。
本指南提供了一个诚实的框架,用于在整体架构和微服务架构之间进行选择,包括通常对成长型企业最有意义的混合方法。
要点
- 从整体开始,除非您有独立扩展或部署的特定且经过验证的需求
- 团队规模是微服务是否成功的最强预测因素:每个服务至少 3 名工程师
- “模块化单体”模式提供了大多数微服务优势,而无需运营开销
- 从整体迁移到微服务应该是增量的,一次提取一项服务
诚实的比较
整体优势
| 优势 | 详情 |
|---|---|
| 简单 | 一个代码库、一个部署、一个数据库 |
| 发展速度 | 无服务间通信开销 |
| 调试 | 一个日志流,堆栈跟踪跨越整个请求 |
| 测试 | 针对单个进程运行集成测试 |
| 重构 | IDE 重构适用于整个代码库 |
| 交易一致性 | 数据库事务自然地跨越所有操作 |
微服务优势
| 优势 | 详情 |
|---|---|
| 独立缩放 | 扩展热服务而不扩展冷服务 |
| 技术多样性 | 针对每个问题使用最好的语言/框架 |
| 团队自治 | 团队独立拥有和部署他们的服务 |
| 故障隔离 | 一项服务故障不会导致整个系统崩溃 |
| 独立部署 | 将更改部署到一项服务而不影响其他服务 |
微服务成本(经常被低估)
| 成本 | 影响 |
|---|---|
| 网络延迟 | 每个服务调用都会增加 1-10 毫秒,跨链相乘 |
| 数据一致性 | 分布式事务复杂;最终一致性令人困惑 |
| 运营费用 | 每个服务的部署管道、监控、日志记录 |
| 测试复杂性 | 集成测试需要运行多个服务 |
| 调试难点 | 请求跨多个服务,日志分布式 |
| 基础设施成本 | 负载均衡器、服务发现、每个服务的 API 网关 |
决策框架
由团队规模决定
| 团队规模 | 推荐 | 推理 |
|---|---|---|
| 1-5名工程师 | 巨石 | 没有足够的人员来维护多项服务 |
| 5-15 名工程师 | 模块化整体结构 | 无需运营成本即可实现未来开采的结构 |
| 15-50 名工程师 | 选择性微服务 | 在已证明可扩展或部署需要的地方提取服务 |
| 50+ 工程师 | 完整的微服务 | 团队自治和独立部署变得至关重要 |
根据扩展需求做出决定
| 场景 | 推荐 |
|---|---|
| 跨功能的均匀负载 | Monolith(扩展整个事物) |
| 一项热门功能,冷落 | 将热门功能提取为服务 |
| 具有不同缩放模式的多个功能 | 用于独立扩展功能的微服务 |
| 流量爆发(闪购) | 针对流量敏感组件的自动缩放微服务 |
根据部署需求决定
| 场景 | 推荐 |
|---|---|
| 每周一起部署所有内容 | 巨石 |
| 一个团队每天部署一次,其他团队每周部署一次 | 提取快速部署团队的代码 |
| 每个团队独立部署 | 微服务 |
| 合规性要求敏感功能的隔离部署 | 受监管组件的微服务 |
模块化整体架构:两全其美
模块化单体将代码组织成单个可部署单元内的边界明确的模块。模块通过定义的接口进行通信,而不是直接数据库访问。
架构
Single Deployment Unit
+---------------------------------------------------+
| [Orders Module] [Inventory Module] [Users Module] |
| | | | |
| +------ Internal API Layer ----------+ |
| | | | |
| [Orders DB] [Inventory DB] [Users DB] |
| | | | |
| +------ Shared Database Server ------+ |
+---------------------------------------------------+
NestJS 模块化整体模式
// orders/orders.module.ts
@Module({
imports: [
InventoryModule, // Explicit dependency declaration
UsersModule,
],
controllers: [OrdersController],
providers: [OrdersService],
exports: [OrdersService], // Controlled public interface
})
export class OrdersModule {}
// inventory/inventory.module.ts
@Module({
controllers: [InventoryController],
providers: [InventoryService],
exports: [InventoryService], // Only expose what others need
})
export class InventoryModule {}
模块规则:
- 模块通过导出的服务进行通信,而不是通过直接数据库访问
- 每个模块独占其数据库表 3.共享数据是通过服务方法访问的,而不是跨模块边界的JOIN
- 模块依赖关系在
imports数组中是明确的
何时将模块提取到服务中
提取时间:
- 模块需要独立扩展(例如图像处理、搜索)
- 该模块的部署频率与其他模块显着不同
- 该模块由单独的团队维护
- 该模块具有不同的技术要求(例如,Python 中的 ML 模型)
在以下情况下请勿提取:
- “看来这应该是一项服务”
- 你想要一个更干净的架构(重构整体架构)
- 您尚未确定具体的扩展或部署需求
迁移策略:整体架构到微服务
绞杀者无花果图案
逐渐用微服务取代单体功能,将流量路由到新服务,而旧代码仍然作为后备。
第 1 步:确定提取候选者(最高的扩展需求或部署摩擦)
第 2 步:与整体架构一起构建新服务
步骤 3:通过 API 网关将流量路由到新服务
步骤 4:通过并行运行来验证正确性
第 5 步:从整体中删除旧代码
数据迁移注意事项
| 方法 | 描述 | 风险 | 时间轴 |
|---|---|---|---|
| 共享数据库(临时) | 新服务读取/写入相同的数据库 | 模式耦合 | 周 |
| 每个服务的数据库 + 同步 | 每个服务都拥有自己的数据,异步同步 | 最终一致性 | 几个月 |
| 事件溯源 | 发布事件,服务打造自己的观点 | 复杂性 | 几个月 |
建议:在迁移过程中从共享数据库开始,然后在服务边界得到证明后转向每个服务数据库。
真实世界的架构示例
电子商务平台
Modular Monolith (recommended for most):
- Product catalog module
- Cart and checkout module
- Order management module
- User accounts module
- Inventory module
All in one deployable unit, backed by one PostgreSQL instance.
Selective Microservices (for high-traffic stores):
- Search service (Elasticsearch, scales independently)
- Image processing service (CPU-intensive, different scaling)
- Payment service (PCI compliance boundary)
Everything else stays in the monolith.
ERP 系统(Odoo 风格)
Monolith is the correct choice for ERP:
- Deep cross-module data relationships
- Complex business rules spanning modules
- Consistent reporting across all data
- Smaller concurrent user counts
- Transactional consistency critical
Odoo itself is a modular monolith: modules are installed/uninstalled,
but everything runs in one process with one database.
常见问题
我们的巨石是否阻碍了我们?
可能不会,除非您有特定瓶颈的证据。如果部署缓慢,请投资 CI/CD。如果某个组件需要扩展,请将其提取出来。如果团队互相踩踏,请强制执行模块边界。大多数“整体问题”实际上是微服务无法解决的代码组织问题——它们只会分发它们。
多少个微服务才算太多?
实际限制:每个负责运营的工程师不超过 3-5 项服务。一个由 5 名工程师组成的团队不应拥有超过 15-25 个服务。除此之外,运营开销占主导地位,工程速度下降。许多成功的公司运行 5-10 个定义明确的服务,而不是数百个纳米服务。
我们可以在一个整体中为不同的模块使用不同的数据库吗?
是的,这就是模块化整体方法。每个模块可以使用单独的模式,甚至可以在同一可部署单元中使用单独的数据库实例。这保留了数据所有权边界,而无需单独服务的运营成本。它还使未来的提取更加容易。
ECOSIRE 如何为客户解决这个问题?
我们建议大多数客户从模块化整体开始。我们的Odoo 实施服务 使用 Odoo 的模块化架构,我们的定制开发项目遵循 NestJS 模块化整体模式。仅当已证明需要独立扩展时,我们才会提取服务——通常是搜索、文件处理或外部集成。请参阅我们的 DevOps 指南 了解完整的架构理念。
接下来会发生什么
架构决策是基础。选择方法后,投资 CI/CD 自动化 以实现可靠部署,投资 监控 以实现运营可见性,并投资 API 网关模式 以管理服务间通信。
联系 ECOSIRE 获取架构咨询,或探索我们的 Odoo 实施服务 以获得可随您的业务扩展的 ERP 架构。
由 ECOSIRE 发布——帮助企业选择适合其发展阶段的架构。
作者
ECOSIRE Research and Development Team
在 ECOSIRE 构建企业级数字产品。分享关于 Odoo 集成、电商自动化和 AI 驱动商业解决方案的洞见。