本文目录导读:

在PHP中抽象服务层,核心目标是解耦业务逻辑与表现层、数据访问层,提升代码的可维护性、可测试性和可扩展性,以下是系统化的抽象方法和最佳实践:
服务层的核心定位
Controller(控制器) → Service(服务层) → Repository(仓储层) → Model(模型)
- 控制器:处理HTTP请求、参数验证、返回响应
- 服务层:业务逻辑、事务管理、领域规则
- 仓储层:数据持久化、查询封装
- 模型:数据结构定义
服务层抽象的基础架构
1 基础服务接口
<?php
namespace App\Services\Contracts;
use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\ModelNotFoundException;
/**
* 通用的 CRUD 服务接口
*/
interface BaseServiceInterface
{
/**
* 列表查询
* @param array $filters
* @param array $options
* @return Collection
*/
public function list(array $filters = [], array $options = []): Collection;
/**
* 详情查询
* @param int $id
* @return object
* @throws ModelNotFoundException
*/
public function find(int $id): object;
/**
* 创建数据
* @param array $data
* @return object
*/
public function create(array $data): object;
/**
* 更新数据
* @param int $id
* @param array $data
* @return object
*/
public function update(int $id, array $data): object;
/**
* 删除数据
* @param int $id
* @return bool
*/
public function delete(int $id): bool;
}
2 抽象基类
<?php
namespace App\Services;
use App\Repositories\Contracts\BaseRepositoryInterface;
use App\Services\Contracts\BaseServiceInterface;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\DB;
/**
* 抽象服务基类
*/
abstract class BaseService implements BaseServiceInterface
{
protected $repository;
/**
* 构造注入仓储层
*/
public function __construct(BaseRepositoryInterface $repository)
{
$this->repository = $repository;
}
public function list(array $filters = [], array $options = []): Collection
{
// 统一处理过滤条件
$query = $this->repository
->queryBuilder()
->when(isset($filters['search']), function ($q) use ($filters) {
$this->applySearch($q, $filters['search']);
});
// 排序
$sortField = $options['sort_field'] ?? 'id';
$sortOrder = $options['sort_order'] ?? 'desc';
$query->orderBy($sortField, $sortOrder);
// 分页/限制
$limit = $options['limit'] ?? null;
if ($limit) {
return $query->limit($limit)->get();
}
return $query->get();
}
public function find(int $id): object
{
$record = $this->repository->find($id);
if (!$record) {
throw new ModelNotFoundException('Record not found');
}
return $record;
}
public function create(array $data): object
{
try {
DB::beginTransaction();
$record = $this->repository->create($data);
// 可在此添加创建后的逻辑
$this->afterCreate($record, $data);
DB::commit();
return $record;
} catch (\Throwable $e) {
DB::rollBack();
throw $e;
}
}
public function update(int $id, array $data): object
{
try {
DB::beginTransaction();
$record = $this->repository->update($id, $data);
$this->afterUpdate($record, $data);
DB::commit();
return $record;
} catch (\Throwable $e) {
DB::rollBack();
throw $e;
}
}
public function delete(int $id): bool
{
try {
DB::beginTransaction();
$result = $this->repository->delete($id);
$this->afterDelete($id);
DB::commit();
return $result;
} catch (\Throwable $e) {
DB::rollBack();
throw $e;
}
}
// 模板方法:可被子类覆写
protected function applySearch($query, $search): void
{
// 子类实现具体的搜索逻辑
}
protected function afterCreate($record, $data): void {}
protected function afterUpdate($record, $data): void {}
protected function afterDelete($id): void {}
}
业务服务示例
1 用户服务
<?php
namespace App\Services;
use App\Repositories\UserRepository;
use App\Services\Contracts\BaseServiceInterface;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Cache;
class UserService extends BaseService
{
protected $userRepository;
public function __construct(UserRepository $repository)
{
parent::__construct($repository);
$this->userRepository = $repository;
}
/**
* 注册新用户 - 包含额外业务逻辑
*/
public function register(array $data): object
{
// 密码加密
$data['password'] = Hash::make($data['password']);
// 创建用户
$user = $this->create($data);
// 发送欢迎邮件
try {
Mail::to($user->email)->send(new WelcomeMail($user));
} catch (\Exception $e) {
// 邮件失败不影响注册,记录日志
Log::warning('Welcome email failed', ['user_id' => $user->id]);
}
return $user;
}
/**
* 用户登录
*/
public function login(string $email, string $password): ?array
{
$user = $this->repository->findByEmail($email);
if (!$user || !Hash::check($password, $user->password)) {
return null;
}
// 生成 token(假设使用 Passport/Sanctum)
$token = $user->createToken('auth_token')->plainTextToken;
// 缓存用户信息
Cache::put("user:{$user->id}", $user, now()->addHours(24));
return [
'user' => $user,
'token' => $token
];
}
/**
* 更新用户资料(带数据验证)
*/
public function updateProfile(int $userId, array $data): object
{
// 业务规则:邮箱唯一性验证
$emailExists = $this->repository->findByEmail($data['email'] ?? '');
if ($emailExists && $emailExists->id !== $userId) {
throw new \InvalidArgumentException("Email already exists");
}
$user = $this->update($userId, $data);
// 更新缓存
Cache::put("user:{$userId}", $user, now()->addHours(24));
return $user;
}
protected function applySearch($query, $search): void
{
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
protected function afterDelete($id): void
{
// 删除用户的关联缓存
Cache::forget("user:{$id}");
}
}
依赖注入与服务容器(Laravel 示例)
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Repositories\UserRepository;
use App\Services\UserService;
use App\Services\Contracts\BaseServiceInterface;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
// 绑定接口到实现
$this->app->bind(BaseServiceInterface::class, UserService::class);
// 绑定具体服务
$this->app->singleton(UserService::class, function ($app) {
return new UserService(
$app->make(UserRepository::class)
);
});
// 自动绑定(如果使用自动解析)
// $this->app->resolving(UserService::class, function ($service, $app) {
// $service->setDependencies($app);
// });
}
public function boot()
{
//
}
}
控制器中使用服务层
<?php
namespace App\Http\Controllers;
use App\Services\UserService;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
class UserController extends BaseController
{
protected $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function index(Request $request): JsonResponse
{
$filters = $request->only(['search', 'role']);
$users = $this->userService->list($filters, [
'sort_field' => $request->get('sort_field', 'id'),
'limit' => $request->get('limit', 10)
]);
return $this->successResponse($users);
}
public function show(int $id): JsonResponse
{
try {
$user = $this->userService->find($id);
return $this->successResponse($user);
} catch (\Exception $e) {
return $this->errorResponse($this->statusNotFound);
}
}
public function store(Request $request): JsonResponse
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:8',
]);
try {
$user = $this->userService->register($validated);
return $this->createdResponse($user);
} catch (\Throwable $e) {
return $this->errorResponse($this->statusServerError, $e->getMessage());
}
}
}
架构模式对比
| 模式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 基础 CRUD | 简单直接 | 业务逻辑易膨胀 | 简单管理系统 |
| 领域服务 | 聚合领域逻辑 | 需要领域设计经验 | 复杂业务系统 |
| CQRS | 读写分离清晰 | 复杂度高 | 高并发的读写系统 |
| Action 模式 | 单一职责极度清晰 | 文件数量多 | 需要流程图展示的场景 |
最佳实践建议
1 接口设计原则
// ❌ 反例:接口过于宽泛 public function getUserData(int $id, array $params = []); public function saveUser(array $data); // ✅ 正例:单一职责 public function fetchUserProfile(int $userId): UserProfile; public function updateUserCredentials(int $userId, string $password): void;
2 错误处理策略
// 统一异常处理
class ServiceException extends \Exception {
public function __construct(
string $message,
protected int $statusCode = 400,
array $context = []
) {
parent::__construct($message);
}
}
// 在服务中使用
public function updateBalance(int $userId, float $amount): void
{
if ($amount <= 0) {
throw new ServiceException("Invalid amount");
}
if (!$this->userHasEnoughBalance($userId, $amount)) {
throw new ServiceException("Insufficient balance", 422);
}
}
3 缓存策略
// 可缓存的服务方法
public function list(array $filters): Collection
{
$cacheKey = $this->generateCacheKey($filters);
return Cache::remember($cacheKey, 3600, function () use ($filters) {
return parent::list($filters);
});
}
// 使缓存失效
protected function afterUpdate($record, $data): void
{
Cache::forget("user:{$record->id}");
Cache::forget("user:all");
}
进阶抽象:领域服务
<?php
namespace App\Services\Domain;
use App\Repositories\OrderRepository;
use App\Repositories\InventoryRepository;
use App\Services\Domain\Contracts\OrderDomainServiceInterface;
/**
* 订单领域服务 - 处理复杂业务规则
*/
class OrderDomainService implements OrderDomainServiceInterface
{
public function __construct(
private OrderRepository $orderRepository,
private InventoryRepository $inventoryRepository
) {}
public function placeOrder(array $items, int $customerId): Order
{
// 检查库存
foreach ($items as $item) {
if (!$this->inventoryRepository->hasStock($item['product_id'], $item['quantity'])) {
throw new \RuntimeException("Insufficient stock");
}
}
// 创建订单
$order = $this->orderRepository->create([
'customer_id' => $customerId,
'status' => OrderStatus::PENDING,
]);
// 扣减库存(事务操作)
DB::transaction(function () use ($items, $order) {
foreach ($items as $item) {
$order->items()->create($item);
$this->inventoryRepository->decrementStock(
$item['product_id'],
$item['quantity']
);
}
});
return $order->load('items');
}
public function cancelOrder(int $orderId): Order
{
return DB::transaction(function () use ($orderId) {
$order = $this->orderRepository->find($orderId);
if ($order->status === OrderStatus::SHIPPED) {
throw new \InvalidArgumentException("Cannot cancel shipped order");
}
// 恢复库存
foreach ($order->items as $item) {
$this->inventoryRepository->incrementStock(
$item->product_id,
$item->quantity
);
}
$order->status = OrderStatus::CANCELLED;
$order->save();
return $order;
});
}
}
测试服务层
<?php
namespace Tests\Unit;
use App\Services\UserService;
use App\Repositories\UserRepository;
use Mockery;
use Tests\TestCase;
class UserServiceTest extends TestCase
{
public function testRegisterCreatesUserAndHashesPassword()
{
// Mock 仓储
$repositoryMock = Mockery::mock(UserRepository::class);
$repositoryMock->shouldReceive('create')
->once()
->withArgs(function ($data) {
// 验证密码已加密
return Hash::needsRehash($data['password']) === false;
})
->andReturn(new User(['id' => 1, 'name' => 'Test User']));
$service = new UserService($repositoryMock);
$user = $service->register([
'name' => 'Test User',
'email' => 'test@example.com',
'password' => 'password123'
]);
$this->assertInstanceOf(User::class, $user);
$this->assertEquals(1, $user->id);
}
public function testLoginReturnsTokenForValidCredentials()
{
// 测试登录逻辑
$user = new User(['email' => 'test@example.com']);
$user->password = Hash::make('secret123');
$repositoryMock = Mockery::mock(UserRepository::class);
$repositoryMock->shouldReceive('findByEmail')
->once()
->with('test@example.com')
->andReturn($user);
$service = new UserService($repositoryMock);
$result = $service->login('test@example.com', 'secret123');
$this->assertArrayHasKey('user', $result);
$this->assertArrayHasKey('token', $result);
}
}
设计原则
- 单一职责:一个服务只做一类事
- 开闭原则:通过扩展服务类而非修改核心逻辑
- 依赖倒置:依赖于抽象接口,不依赖具体实现
- 接口隔离:服务接口面向调用者需求设计
- 迪米特法则:服务不直接操作其他服务的内部细节
服务层的抽象是一个持续演进的过程,建议从简单的 BaseService 开始,随着业务复杂度增加逐步引入领域服务、CQRS 等高级模式,始终保持清晰的职责边界和可测试性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播