该拆单体项目了,因模块耦合导致修改一处需全量测试;应先显性化隐式依赖、解耦上帝对象、接口先行,再按业务切分并确保包可独立演进。

模块耦合到改一处要测全量,就该拆了
单体项目不是不能大,而是当一次 git push 后 CI 跑 20 分钟、一个 utils.py 修改要连带验证支付、订单、通知三个业务线的逻辑时,说明边界已经模糊。这时候拆不是为了“微服务”,是为让修改可预测。
- 典型信号:
import链路过深(比如from core.services.payment.adapters import AlipayClient却在用户注册模块里被调用) - 测试失焦:跑
test_user_signup.py时,pytest自动加载了数据库迁移脚本和 Redis 连接池初始化逻辑 - 部署牵连:改了个日志格式,却要走完整个发布流水线,因为所有模块共用同一个
Dockerfile和requirements.txt
依赖倒置没落地,硬拆只会更乱
直接按业务切分目录结构(比如新建 /orders/、/notifications/)但保留所有跨模块直调,等于给单体套了个文件夹马甲。真正要先做的,是把隐式依赖显性化。
- 检查
settings.py或配置加载逻辑:是否存在模块 A 通过读取全局config.EMAIL_PROVIDER去决定行为,而不是接收一个email_sender: EmailSenderProtocol实例 - 识别“上帝对象”:比如一个叫
AppContext的类,被 17 个模块 import 并调用其.get_db_session()、.get_cache_client()、.get_logger()—— 这是拆分前必须解耦的锚点 - 接口先行:在拆出新包前,先在原项目里定义
class NotificationService(Protocol),让现有代码只依赖它,再实现移到新包
setup.py / pyproject.toml 没配对,本地 import 就会炸
拆完发现 import notifications 报 ModuleNotFoundError?大概率是 Python 解释器根本没把新包加进 sys.path。这不是路径问题,是分发配置问题。
- 别用
pip install -e .之前手动改PYTHONPATH:临时方案会掩盖真实依赖关系 -
pyproject.toml里必须有对应配置,例如新包myapp-notifications要声明:[project]下的name = "myapp-notifications",且[project.optional-dependencies]中明确列出它被谁依赖 - 验证方式:在虚拟环境外执行
python -c "import notifications; print(notifications.__file__)",输出路径应指向已安装的 egg-info 目录,而非源码目录
数据库事务跨边界,拆完数据就错乱
原来一个函数里 session.add(order); session.add(log); session.commit() 很稳,拆成订单服务调用通知服务后,order 写入成功但通知失败,没人回滚——这不是分布式事务难题,是本地事务边界误判。
立即学习“Python免费学习笔记(深入)”;
- 禁止跨服务调用中隐式共享
Session:哪怕两个包都在同一进程,也绝不传session对象过去 - 用事件替代直调:订单创建后发
OrderCreatedEvent到本地队列(如queue.Queue),通知模块监听并自行开启新事务处理 - 如果真要强一致性,接受“应用层补偿”:订单服务记录
notification_pending状态,通知模块成功后回调标记完成;失败则由定时任务扫描补发
最常被跳过的一步,是确认每个拆出的包是否真的能独立演进——比如改了 notifications 的重试策略,不发版 orders 就能生效。做不到这点,拆只是把泥球搓成了几个小泥球。










