Services 与 Contract
这页讲的是业务层怎么落。
先用最常见的理解方式就够:
Contract放接口Services写实现Dto放传输对象Controller调契约
先把边界分清
Contract 负责定义服务边界。
Services 负责把业务流程串起来。
最稳的理解方式是:
- Controller 不写业务
- Service 不直接碰数据库底层
- Repository 负责数据访问
一个最小落地方式
1. 在 Component.deps.json 里启用 BusinessServices
{
"Components": {
"Services": [
"BusinessServices"
]
}
}
2. 契约接口继承 IBusinessService
public interface IUserContract : IBusinessService
{
Task<bool> ChangeUser(int id);
Task<bool> ChangeUserStatus(int id, int statusCode);
}
3. Service 实现契约
public class UserService : IUserContract
{
private readonly UserRepository _userRepository;
public UserService(UserRepository userRepository)
{
_userRepository = userRepository;
}
public async Task<bool> ChangeUser(int id)
{
var user = await _userRepository.GetByIdAsync(id);
return await _userRepository.UpdateAsync(user);
}
public Task<bool> ChangeUserStatus(int id, int statusCode)
{
return Task.FromResult(true);
}
}
4. Controller 注入契约
public class UserController : ApiControllerBase
{
private readonly IUserContract _userService;
public UserController(IUserContract userService)
{
_userService = userService;
}
}
这一层现在怎么理解更合适
如果项目不大,把 Services 理解成“业务服务层”就够了。
如果项目开始变复杂,就把它看成一个大的 DDD 业务领域层。
也就是说,Services 下面可以继续拆:
- 应用服务
- 领域服务
- Rules
- Strategies
但有一条别变:
数据访问还是收口到 Repository。
一个常见的组织方式
XXX.Services
├── PatientSettlement
│ ├── PatientSettlementService.cs
│ ├── SettlementRuleDomainService.cs
│ ├── Rules
│ └── Strategies
├── PrescriptionAudit
│ ├── PrescriptionAuditService.cs
│ ├── AuditRuleDomainService.cs
│ └── Rules
怎么判断一个类该放哪
可以先问自己两个问题:
-
这个类是不是在组织一次完整业务流程?
如果是,更像应用服务。 -
这个类拿掉仓储、事务、DTO 之后,还像一段独立业务规则吗?
如果是,更像领域服务。
ServiceResult 和 PagedResult 怎么选
PagedResult<T>
分页查询就用它。
ServiceResult<T>
只有在分支复杂、需要统一包装业务结果时再用。
能直接返回 DTO、列表、分页结果的时候,先别把它包得太重。
这一层最容易写错的地方
- 契约接口没有继承
IBusinessService BusinessServices没开- Service 直接去碰
IFreeSql - 把 Controller 的请求模型直接塞进业务层逻辑
- 明明能直接返回 DTO,却把所有东西都包成
ServiceResult
最后记一条
Services 这一层最重要的,不是“类名是不是漂亮”,而是边界是不是清楚:
- Service 负责业务流程
- Repository 负责数据访问
- Controller 负责接口入口
只要这条没乱,后面怎么继续 DDD 化都好调整。