Skip to content

服务职责划分指南

📋 文档说明

本文档详细说明了 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
    │       └─
    └─

快速判断表

操作类型使用服务示例方法
创建活动ActivityApplicationServicecreateActivity()
发布活动ActivityApplicationServicepublishActivity()
取消活动ActivityApplicationServicecancelActivity()
查询活动详情ActivityQueryApplicationServicegetActivityById()
查询活动列表ActivityQueryApplicationServicegetUserCreatedActivities()
活动统计ActivityQueryApplicationServicecountTotalActivities()
申请加入活动ActivityMemberApplicationServiceapplyToJoinActivity()
审核加入申请ActivityMemberApplicationServiceapproveJoinRequest()
查询成员列表ActivityMemberApplicationServicegetActivityMembers()

🚫 常见错误

❌ 错误 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);
    }
}

📚 相关文档


📝 总结

关键要点

  1. 命令与查询分离: 写操作用 ApplicationService,读操作用 QueryApplicationService
  2. 单一职责: 每个服务只负责一个业务领域
  3. 事务边界: 命令服务管理事务,查询服务使用只读事务
  4. 性能优化: 查询服务适合添加缓存,命令服务负责清除缓存
  5. 可扩展性: 为事件驱动、读写分离等高级特性预留空间

开发检查清单

  • [ ] 新增写操作时,是否放在了命令服务中?
  • [ ] 新增查询时,是否放在了查询服务中?
  • [ ] 命令方法是否添加了 @Transactional 注解?
  • [ ] 查询方法是否避免了改变系统状态?
  • [ ] 是否遵循了服务的职责边界?
  • [ ] Controller 是否调用了正确的服务?

文档版本: v1.0
最后更新: 2025-11-14
维护者: Tour Mate Platform Team

Powered by VitePress