2023年Stack Overflow调研显示,67%的.NET开发者仍在把仓储接口和实现塞在同一层。这不是技术债,是架构认知的代际断层。

基础设施层(Infrastructure Layer)被戏称为"最无聊的层",却藏着软件设计中最持久的争议。它本该是技术细节的隔离带,却成了耦合的重灾区。

从N层到六边形:一场持续20年的迁徙

从N层到六边形:一场持续20年的迁徙

早期的N层架构里,基础设施层是链条末端。业务层直接操作数据库对象,仓储接口和实现挤在同一层——这在CRUD时代完全够用。

但现代系统的技术栈复杂度翻了十倍。消息队列、外部API、缓存、对象存储、日志框架……每个都是潜在的泄漏点。

六边形架构(Hexagonal Architecture)和整洁架构(Clean Architecture)的出现,本质上是一次"方向反转":让业务核心成为宇宙中心,基础设施变成可替换的插件。

关键转变:仓储接口被抽离到领域层(Domain Layer)或应用层(Application Layer),基础设施层只保留实现。

这解决了三个顽疾:业务逻辑可以独立编译测试、技术细节不再向上渗透、替换数据库时不用重写业务代码。

那些藏在代码库里的"智能陷阱"

那些藏在代码库里的"智能陷阱"

我见过最隐蔽的反模式是"智能仓储"(Smart Repository)——在仓储里塞业务逻辑,比如自动计算折扣后存储、或者根据状态触发副作用。

表面看是代码复用,实际是职责错位。仓储变成了披着技术外衣的业务层,测试时需要连数据库一起启动,单元测试沦为集成测试。

另一个常见坑是"领域泄漏的工具类"。一个处理货币计算的`MoneyUtils`,内部偷偷调用了外部汇率服务。业务代码以为自己在用纯函数,实际上被网络延迟和第三方故障绑架。

框架驱动设计(Framework-Driven Design)更隐蔽。团队选了某个ORM后,实体类被迫继承框架基类、属性被打上特性标签(Attribute)。领域模型沦为持久化模型的附庸,换ORM等于重写核心业务。

边界划分的实战法则

边界划分的实战法则

判断接口归属有个朴素标准:谁需要依赖它?

应用层需要持久化数据 → 仓储接口属于应用层或领域层。基础设施层实现接口 → 依赖关系向内指向核心,而非向外扩散。

端口与适配器(Ports and Adapters)的命名很贴切:业务核心定义"我要什么"(端口),基础设施回答"我怎么做"(适配器)。

日志和缓存这类横切关注点更微妙。如果日志格式包含业务语义(如"用户{userId}下单失败"),接口应由应用层定义;纯技术日志(如SQL执行时间)则可留在基础设施层。

外部服务调用是重灾区。我见过团队在基础设施层直接返回供应商DTO,业务层被迫消化第三方字段命名。正确的切口:基础设施层做防腐转换(Anti-Corruption Layer),进出都是领域对象。

测试策略是检验标准。理想的领域层测试应该能在内存中跑完,启动时间以毫秒计。如果跑测试需要Docker Compose,说明边界画错了。

一个仍在演进的共识

一个仍在演进的共识

整洁架构提出者Robert C. Martin近年修正过早期观点:接口定义的位置没有绝对答案,取决于变更频率和团队规模。

小团队、单体应用、技术栈稳定——接口放基础设施层,减少抽象层数。多团队、微服务、技术栈可能替换——接口必须靠近业务核心。

这不是非黑即白的选择,是权衡后的刻意设计。

微软eShopOnContainers示例项目把仓储接口放在应用层,基础设施层用EF Core实现。但GitHub上有个高星Fork把接口下沉到领域层,因为团队需要支持MongoDB和SQL Server双写——同一套代码,不同边界决策。

你的代码库里,仓储接口现在躺在哪一层?最近一次替换数据库或框架时,你改了百分之多少的文件?