服务职责划分指南
📋 文档说明
本文档详细说明了 Tour Mate Platform 项目中各个应用服务的职责划分,帮助开发者理解每个服务的边界和使用场景。
文档版本: v1.0
最后更新: 2025-11-14
适用范围: App 层所有应用服务
🎯 核心原则
1. CQRS(命令查询职责分离)
命令(Command):改变系统状态的操作
查询(Query):不改变系统状态的操作为什么要分离?
- ✅ 职责清晰,便于理解和维护
- ✅ 读写可以独立优化(缓存、读写分离)
- ✅ 便于后续引入事件溯源等高级特性
- ✅ 降低代码耦合度
2. 单一职责原则
每个服务只负责一个业务领域,不跨界处理其他领域的业务。
3. 依赖倒置原则
服务依赖于 Domain 层定义的接口,而不是具体实现。
📊 服务全景图
┌─────────────────────────────────────────────────────────────┐
│ Trigger 层 (Controller) │
│ 负责协议转换和参数校验 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────┼─────────────────────┐
↓ ↓ ↓
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 命令服务 │ │ 查询服务 │ │ 成员管理 │
│ Application │ │ Query │ │ Member │
│ Service │ │ Application │ │ Application │
│ │ │ Service │ │ Service │
└──────────────┘ └──────────────┘ └──────────────┘
↓ ↓ ↓
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 写操作 │ │ 读操作 │ │ 成员操作 │
│ + 事务管理 │ │ + 数据组装 │ │ + 权限检查 │
└──────────────┘ └──────────────┘ └──────────────┘🔧 活动相关服务
1. ActivityApplicationService(活动命令服务)
包路径: com.alisunxin.api.service.ActivityApplicationService
职责定位
- ✅ 处理活动的写操作(命令)
- ✅ 管理事务边界
- ✅ 协调领域对象完成业务流程
- ✅ 发布领域事件
核心方法
| 方法名 | 说明 | 事务 | 返回值 |
|---|---|---|---|
createActivity() | 创建活动 | ✅ | String (活动ID) |
publishActivity() | 发布活动 | ✅ | void |
cancelActivity() | 取消活动 | ✅ | void |
joinActivity() | 参加活动 | ✅ | boolean |
quitActivity() | 退出活动 | ✅ | boolean |
startActivity() | 开始活动 | ✅ | void |
completeActivity() | 完成活动 | ✅ | void |
特征
- 所有方法都有
@Transactional注解 - 方法会改变系统状态
- 可能发布领域事件(TODO)
- 不包含任何查询方法
使用示例
java
@RestController
public class ActivityController {
@Resource
private ActivityApplicationService activityApplicationService;
@PostMapping("/create")
public Response<String> createActivity(@RequestBody CreateActivityRequest request) {
// 调用命令服务创建活动
String activityId = activityApplicationService.createActivity(
userId, title, description, location,
startTime, endTime, maxParticipants, tags
);
return Response.success(activityId);
}
@PostMapping("/publish")
public Response<Void> publishActivity(@RequestBody PublishActivityRequest request) {
// 调用命令服务发布活动
activityApplicationService.publishActivity(activityId, userId);
return Response.success();
}
}依赖注入
java
@Resource
private IActivityAggregateRepository activityAggregateRepository;
@Resource
private ActivityCreationService activityCreationService;2. ActivityQueryApplicationService(活动查询服务)
包路径: com.alisunxin.api.service.ActivityQueryApplicationService
职责定位
- ✅ 处理活动的读操作(查询)
- ✅ 多表关联查询
- ✅ 数据聚合和转换
- ✅ 适合添加缓存优化
核心方法
2.1 基础查询
| 方法名 | 说明 | 返回值 |
|---|---|---|
getActivityById() | 根据ID查询活动详情 | Optional<ActivityAggregate> |
getUserCreatedActivities() | 查询用户创建的活动列表 | List<ActivityAggregate> |
getUserParticipatedActivities() | 查询用户参与的活动列表 | List<ActivityAggregate> |
getUserActiveActivities() | 查询用户活跃的活动列表 | List<ActivityAggregate> |
2.2 状态筛选
| 方法名 | 说明 | 返回值 |
|---|---|---|
getUpcomingActivities() | 查询即将开始的活动 | List<ActivityAggregate> |
getPublishedActivities() | 查询已发布的活动 | List<ActivityAggregate> |
getInProgressActivities() | 查询进行中的活动 | List<ActivityAggregate> |
getCompletedActivities() | 查询已完成的活动 | List<ActivityAggregate> |
2.3 成员查询
| 方法名 | 说明 | 返回值 |
|---|---|---|
getActivityMembers() | 查询活动成员列表 | List<ActivityMemberEntity> |
getActivityApprovedMembers() | 查询已通过成员 | List<ActivityMemberEntity> |
getActivityPendingMembers() | 查询待审核成员 | List<ActivityMemberEntity> |
getActivityCreator() | 查询活动创建者 | Optional<ActivityMemberEntity> |
2.4 统计方法
| 方法名 | 说明 | 返回值 |
|---|---|---|
countTotalActivities() | 统计活动总数 | long |
countActivityMembers() | 统计活动成员数 | long |
countActivityApprovedMembers() | 统计已通过成员数 | long |
countActivityPendingMembers() | 统计待审核成员数 | long |
countUserParticipatedActivities() | 统计用户参与的活动数 | long |
countUserCreatedActivities() | 统计用户创建的活动数 | long |
countUserActiveActivities() | 统计用户活跃的活动数 | long |
2.5 检查方法
| 方法名 | 说明 | 返回值 |
|---|---|---|
hasUserJoinedActivity() | 检查用户是否已加入活动 | boolean |
isUserActivityCreator() | 检查用户是否为活动创建者 | boolean |
activityExists() | 检查活动是否存在 | boolean |
2.6 复杂查询
| 方法名 | 说明 | 返回值 |
|---|---|---|
getActivityDetailWithMembers() | 查询活动详情(包含成员信息) | ActivityDetailVO |
特征
- 方法不改变系统状态
- 没有
@Transactional注解(或使用只读事务) - 可能涉及多表关联查询
- 适合添加缓存(如
@Cacheable)
使用示例
java
@RestController
public class ActivityController {
@Resource
private ActivityQueryApplicationService activityQueryApplicationService;
@GetMapping("/detail/{activityId}")
public Response<ActivityResponse> getActivityDetail(@PathVariable String activityId) {
// 调用查询服务查询活动详情
ActivityAggregate aggregate = activityQueryApplicationService
.getActivityById(activityId)
.orElseThrow(() -> new IllegalArgumentException("活动不存在"));
return Response.success(convertToResponse(aggregate));
}
@GetMapping("/my/created")
public Response<List<ActivityResponse>> getMyCreatedActivities() {
// 调用查询服务查询用户创建的活动
List<ActivityAggregate> aggregates = activityQueryApplicationService
.getUserCreatedActivities(userId);
return Response.success(convertToResponses(aggregates));
}
}依赖注入
java
@Resource
private IActivityAggregateRepository activityAggregateRepository;
@Resource
private IActivityMemberRepository activityMemberRepository;3. ActivityMemberApplicationService(活动成员管理服务)
包路径: com.alisunxin.api.service.ActivityMemberApplicationService
职责定位
- ✅ 处理活动成员的管理操作
- ✅ 成员加入、退出、移除
- ✅ 成员申请审核
- ✅ 成员列表查询和统计
核心方法
3.1 命令方法(写操作)
| 方法名 | 说明 | 事务 | 返回值 |
|---|---|---|---|
applyToJoinActivity() | 申请加入活动 | ✅ | String (成员ID) |
approveJoinRequest() | 审核加入申请 | ✅ | void |
leaveActivity() | 退出活动 | ✅ | void |
removeMember() | 移除成员 | ✅ | void |
3.2 查询方法(读操作)
| 方法名 | 说明 | 返回值 |
|---|---|---|
getActivityMembers() | 查询活动成员列表 | List<ActivityMemberEntity> |
getApprovedMembers() | 查询已通过成员 | List<ActivityMemberEntity> |
getPendingMembers() | 查询待审核成员 | List<ActivityMemberEntity> |
getUserActivities() | 查询用户参与的活动 | List<ActivityMemberEntity> |
getUserActiveActivities() | 查询用户活跃的活动 | List<ActivityMemberEntity> |
getUserHistoryActivities() | 查询用户历史参与的活动 | List<ActivityMemberEntity> |
3.3 统计和检查方法
| 方法名 | 说明 | 返回值 |
|---|---|---|
countActivityMembers() | 统计活动成员数 | long |
countApprovedMembers() | 统计已通过成员数 | long |
countPendingMembers() | 统计待审核成员数 | long |
countUserActivities() | 统计用户参与的活动数 | long |
countUserActiveActivities() | 统计用户活跃的活动数 | long |
hasJoinedActivity() | 检查用户是否已加入活动 | boolean |
isActivityCreator() | 检查用户是否为活动创建者 | boolean |
特征
- 命令方法有
@Transactional注解 - 查询方法不改变系统状态
- 专注于成员管理,不包含活动创建者相关查询
使用示例
java
@RestController
public class ActivityController {
@Resource
private ActivityMemberApplicationService activityMemberApplicationService;
@PostMapping("/join")
public Response<String> joinActivity(@RequestBody JoinActivityRequest request) {
// 调用成员管理服务申请加入活动
String memberId = activityMemberApplicationService.applyToJoinActivity(
activityId, userId, needApproval
);
return Response.success(memberId);
}
@PostMapping("/approve")
public Response<Void> approveJoinRequest(@RequestBody ApproveJoinRequest request) {
// 调用成员管理服务审核加入申请
activityMemberApplicationService.approveJoinRequest(
memberId, creatorId, approved
);
return Response.success();
}
@GetMapping("/members/{activityId}")
public Response<List<MemberResponse>> getActivityMembers(@PathVariable String activityId) {
// 调用成员管理服务查询成员列表
List<ActivityMemberEntity> members = activityMemberApplicationService
.getActivityMembers(activityId);
return Response.success(convertToResponses(members));
}
}依赖注入
java
@Resource
private IActivityMemberRepository activityMemberRepository;
@Resource
private ActivityMemberService activityMemberService;🎨 服务选择决策树
需要操作活动?
├─ 是否改变系统状态?
│ ├─ 是(创建、修改、删除)
│ │ ├─ 是否涉及成员管理?
│ │ │ ├─ 是 → ActivityMemberApplicationService
│ │ │ └─ 否 → ActivityApplicationService
│ │ └─
│ └─ 否(查询、统计、检查)
│ ├─ 是否只查询成员信息?
│ │ ├─ 是 → ActivityMemberApplicationService
│ │ └─ 否 → ActivityQueryApplicationService
│ └─
└─快速判断表
| 操作类型 | 使用服务 | 示例方法 |
|---|---|---|
| 创建活动 | ActivityApplicationService | createActivity() |
| 发布活动 | ActivityApplicationService | publishActivity() |
| 取消活动 | ActivityApplicationService | cancelActivity() |
| 查询活动详情 | ActivityQueryApplicationService | getActivityById() |
| 查询活动列表 | ActivityQueryApplicationService | getUserCreatedActivities() |
| 活动统计 | ActivityQueryApplicationService | countTotalActivities() |
| 申请加入活动 | ActivityMemberApplicationService | applyToJoinActivity() |
| 审核加入申请 | ActivityMemberApplicationService | approveJoinRequest() |
| 查询成员列表 | ActivityMemberApplicationService | getActivityMembers() |
🚫 常见错误
❌ 错误 1: 在命令服务中进行查询
java
// ❌ 错误示例
@RestController
public class ActivityController {
@Resource
private ActivityApplicationService activityApplicationService;
@GetMapping("/detail/{activityId}")
public Response<ActivityResponse> getActivityDetail(@PathVariable String activityId) {
// 错误:在命令服务中查询
ActivityAggregate aggregate = activityApplicationService.getActivityDetail(activityId);
return Response.success(aggregate);
}
}java
// ✅ 正确示例
@RestController
public class ActivityController {
@Resource
private ActivityQueryApplicationService activityQueryApplicationService;
@GetMapping("/detail/{activityId}")
public Response<ActivityResponse> getActivityDetail(@PathVariable String activityId) {
// 正确:使用查询服务
ActivityAggregate aggregate = activityQueryApplicationService
.getActivityById(activityId)
.orElseThrow(() -> new IllegalArgumentException("活动不存在"));
return Response.success(aggregate);
}
}❌ 错误 2: 服务职责越界
java
// ❌ 错误示例:在成员管理服务中查询活动创建者的活动列表
public class ActivityMemberApplicationService {
public List<ActivityMemberEntity> getUserCreatedActivities(String userId) {
// 错误:这不是成员管理的职责
return activityMemberRepository.findCreatedActivitiesByUserId(userId);
}
}java
// ✅ 正确示例:使用活动查询服务
public class ActivityQueryApplicationService {
public List<ActivityAggregate> getUserCreatedActivities(String userId) {
// 正确:活动查询是查询服务的职责
return activityAggregateRepository.findByCreatorId(UserId.of(userId));
}
}❌ 错误 3: 混合命令和查询
java
// ❌ 错误示例:在一个服务中混合命令和查询
public class ActivityApplicationService {
@Transactional
public void publishActivity(String activityId, String userId) {
// 命令操作
}
public List<ActivityAggregate> getActivities() {
// 查询操作 - 不应该在这里
}
}java
// ✅ 正确示例:分离命令和查询
public class ActivityApplicationService {
@Transactional
public void publishActivity(String activityId, String userId) {
// 只包含命令操作
}
}
public class ActivityQueryApplicationService {
public List<ActivityAggregate> getActivities() {
// 查询操作在查询服务中
}
}📈 性能优化建议
1. 查询服务添加缓存
java
@Service
public class ActivityQueryApplicationService {
@Cacheable(value = "activity", key = "#activityId")
public Optional<ActivityAggregate> getActivityById(String activityId) {
return activityAggregateRepository.findById(ActivityId.of(activityId));
}
@Cacheable(value = "userActivities", key = "#userId")
public List<ActivityAggregate> getUserCreatedActivities(String userId) {
return activityAggregateRepository.findByCreatorId(UserId.of(userId));
}
}2. 命令服务清除缓存
java
@Service
public class ActivityApplicationService {
@Transactional
@CacheEvict(value = "activity", key = "#activityId")
public void publishActivity(String activityId, String userId) {
// 发布活动后清除缓存
}
}3. 读写分离
java
// 查询服务使用只读数据源
@Service
public class ActivityQueryApplicationService {
@Transactional(readOnly = true)
public List<ActivityAggregate> getUserCreatedActivities(String userId) {
// 使用只读事务,可以路由到从库
return activityAggregateRepository.findByCreatorId(UserId.of(userId));
}
}🔮 未来扩展
1. 事件驱动架构
在命令服务中发布领域事件:
java
@Service
public class ActivityApplicationService {
@Resource
private ApplicationEventPublisher eventPublisher;
@Transactional
public void publishActivity(String activityId, String userId) {
// 业务逻辑
aggregate.publishActivity();
activityAggregateRepository.update(aggregate);
// 发布领域事件
eventPublisher.publishEvent(new ActivityPublishedEvent(activityId, userId));
}
}2. 异步查询
对于复杂查询,可以使用异步方式:
java
@Service
public class ActivityQueryApplicationService {
@Async
public CompletableFuture<List<ActivityAggregate>> getUserCreatedActivitiesAsync(String userId) {
List<ActivityAggregate> activities = activityAggregateRepository
.findByCreatorId(UserId.of(userId));
return CompletableFuture.completedFuture(activities);
}
}3. 查询对象(Query Object)模式
对于复杂查询条件,可以引入查询对象:
java
@Data
@Builder
public class ActivityQuery {
private String userId;
private ActivityStatus status;
private LocalDateTime startTime;
private LocalDateTime endTime;
private String city;
private List<String> tags;
private Integer page;
private Integer pageSize;
}
@Service
public class ActivityQueryApplicationService {
public PageResult<ActivityAggregate> queryActivities(ActivityQuery query) {
// 根据查询对象进行复杂查询
return activityAggregateRepository.query(query);
}
}📚 相关文档
📝 总结
关键要点
- ✅ 命令与查询分离: 写操作用 ApplicationService,读操作用 QueryApplicationService
- ✅ 单一职责: 每个服务只负责一个业务领域
- ✅ 事务边界: 命令服务管理事务,查询服务使用只读事务
- ✅ 性能优化: 查询服务适合添加缓存,命令服务负责清除缓存
- ✅ 可扩展性: 为事件驱动、读写分离等高级特性预留空间
开发检查清单
- [ ] 新增写操作时,是否放在了命令服务中?
- [ ] 新增查询时,是否放在了查询服务中?
- [ ] 命令方法是否添加了
@Transactional注解? - [ ] 查询方法是否避免了改变系统状态?
- [ ] 是否遵循了服务的职责边界?
- [ ] Controller 是否调用了正确的服务?
文档版本: v1.0
最后更新: 2025-11-14
维护者: Tour Mate Platform Team