服务层

概述

服务层(Service Layer)是 XinAdmin 后端架构中重要的业务逻辑处理层,位于控制器(Controller)和数据仓库(Repository)之间。服务层的主要职责包括:

  • 业务逻辑处理:封装复杂的业务规则和逻辑流程
  • 事务管理:协调多个数据操作确保数据一致性
  • 数据转换:处理控制器和仓库之间的数据转换
  • 服务编排:协调多个仓库或其他服务的调用

XinAdmin 的服务层遵循单一职责原则,每个服务类专注于特定领域的业务逻辑。服务层继承自 BaseService,该基类提供了通用的响应处理和工具方法。

服务层与控制器的交互通过依赖注入实现,使得代码更加松耦合和易于测试。

BaseService

BaseService 是所有服务类的基类,提供了一系列通用方法来简化开发过程。

通用方法

BaseService 提供了以下通用方法:

  1. 树形数据构建

    • 方法:getTreeData(array &$list, int $parentId = 0)
    • 功能:将平面结构的数据转换为树形结构,常用于菜单、分类等层级关系数据的处理
    • 示例:
      $treeData = $this->getTreeData($list, 0);
  2. 响应处理方法 通过引入 RequestJson trait,BaseService 提供了多种响应处理方法:

    • success($data, $message):返回成功的JSON响应
    • error($data, $message):返回失败的JSON响应
    • warn($data, $message):返回警告的JSON响应
    • throwSuccess($data, $message):抛出成功响应并中断程序运行
    • throwError($data, $message):抛出错误响应并中断程序运行
    • notification($msg, $description, $type, $placement):返回通知类型响应

事务处理

虽然 BaseService 本身不直接提供事务管理功能,但服务层可以使用 Laravel 的 DB 门面或模型的事务方法来处理复杂的数据操作:

use Illuminate\Support\Facades\DB;

public function complexOperation() {
    DB::beginTransaction();
    
    try {
        // 执行多个数据库操作
        $result1 = $this->repository->create($data1);
        $result2 = $this->anotherRepository->update($data2);
        
        DB::commit();
        return $this->success(["result1" => $result1, "result2" => $result2]);
    } catch (\Exception $e) {
        DB::rollback();
        return $this->error($e->getMessage());
    }
}

Laravel 还提供了便捷的 DB::transaction() 方法,自动处理提交和回滚:

use Illuminate\Support\Facades\DB;

public function complexOperation() {
    try {
        $results = DB::transaction(function () use ($data1, $data2) {
            $result1 = $this->repository->create($data1);
            $result2 = $this->anotherRepository->update($data2);
            return ["result1" => $result1, "result2" => $result2];
        });
        
        return $this->success($results);
    } catch (\Exception $e) {
        return $this->error($e->getMessage());
    }
}

缓存处理

服务层可以通过 Laravel 的 Cache 门面或缓存服务来实现数据缓存,提升应用性能。常见的缓存模式包括:

  1. 读取缓存优先

    use Illuminate\Support\Facades\Cache;
    
    public function getData($id) {
        $cacheKey = "data_{$id}";
        
        return Cache::remember($cacheKey, 3600, function () use ($id) {
            return $this->repository->find($id);
        });
    }
  2. 更新时清除缓存

    public function updateData($id, $data) {
        $result = $this->repository->update($id, $data);
        
        if ($result) {
            Cache::forget("data_{$id}"); // 清除对应缓存
            Cache::forget('data_list');  // 清除可能相关的列表缓存
        }
        
        return $result;
    }

创建服务

创建新的服务类需要遵循以下步骤:

  1. 确定服务名称:根据业务领域命名,如用户相关服务命名为 UserService

  2. 创建服务文件:服务文件应放置在 app/Services/ 目录下,对于系统相关服务则放在 app/Services/Sys/ 目录

  3. 继承 BaseService:新服务类必须继承 BaseService

    <?php
    
    namespace App\Services\Sys;
    
    use App\Services\BaseService;
    use Illuminate\Http\JsonResponse;
    
    class CustomService extends BaseService
    {
        // 服务实现
    }
  4. 实现业务逻辑:在服务类中实现具体的业务方法

    <?php
    
    namespace App\Services\Sys;
    
    use App\Services\BaseService;
    use App\Repositories\Sys\CustomRepository;
    use Illuminate\Http\JsonResponse;
    
    class CustomService extends BaseService
    {
        protected CustomRepository $repository;
        
        public function __construct()
        {
            $this->repository = new CustomRepository();
        }
        
        public function getList(): JsonResponse
        {
            try {
                $data = $this->repository->getList();
                return $this->success($data);
            } catch (\Exception $e) {
                return $this->error($e->getMessage());
            }
        }
    }

业务逻辑处理

服务层是处理业务逻辑的核心,应该包含:

  1. 数据验证:在处理业务前对输入数据进行验证
  2. 业务规则实现:实现具体的业务规则和逻辑判断
  3. 异常处理:妥善处理可能出现的异常情况
  4. 事务控制:对于涉及多个数据操作的业务,使用事务保证数据一致性

例如,用户注册服务的业务逻辑:

public function registerUser($userData) {
    // 1. 数据验证
    if ($this->userRepository->exists(['email' => $userData['email']])) {
        return $this->error('该邮箱已被注册');
    }
    
    // 2. 业务规则:检查用户数量限制
    if ($this->userRepository->count() > 10000) {
        return $this->error('用户数量已达上限');
    }
    
    // 3. 事务处理:确保用户和相关数据的一致性
    try {
        $result = \DB::transaction(function () use ($userData) {
            // 创建用户
            $user = $this->userRepository->create($userData);
            
            // 创建用户初始配置
            $this->userConfigRepository->create([
                'user_id' => $user['id'],
                'theme' => 'light'
            ]);
            
            return $user;
        });
        
        return $this->success($result, '注册成功');
    } catch (\Exception $e) {
        return $this->error('注册失败:' . $e->getMessage());
    }
}

服务注入

服务类可以通过 Laravel 的依赖注入容器自动注入到控制器中。控制器方法可以直接声明服务作为参数,框架会自动解析并注入相应的服务实例:

<?php

namespace App\Http\Controllers\Sys;

use App\Http\Controllers\Controller;
use App\Services\Sys\CustomService;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;

class CustomController extends Controller
{
    public function index(CustomService $customService): JsonResponse
    {
        return $customService->getList();
    }
    
    public function store(Request $request, CustomService $customService): JsonResponse
    {
        return $customService->create($request->all());
    }
}

最佳实践

  1. 单一职责:每个服务类应专注于特定业务领域,避免功能过于复杂

  2. 保持简洁:服务方法应保持简短和专注,复杂逻辑可以拆分为多个私有方法

  3. 合理使用事务:仅在必要时使用事务,避免长时间锁定数据库资源

  4. 错误处理:提供有意义的错误信息,便于调试和问题排查

  5. 缓存策略:合理使用缓存,注意缓存失效策略

  6. 日志记录:对关键业务操作进行日志记录

  7. 参数验证:在服务层进行必要的业务参数验证

  8. 测试友好:编写易于单元测试的服务代码,避免过多硬编码依赖

遵循这些最佳实践可以确保服务层代码的可维护性、可扩展性和健壮性。