返回首页

后端开发中的 UseCase:结构化业务逻辑

本文解释了后端应用中用于描述业务场景的 UseCase 模式。它涵盖结构、优势、局限性,以及在 PHP 和 Laravel 中的实现示例,包括测试和代码组织。

UseCase:如何为开发者在代码中描述业务流程
Advertisement 728x90

UseCase 模式:后端业务逻辑结构化最佳实践

UseCase 是一种架构模式,用于定义应用中的业务场景。它将代码围绕具体的系统操作组织起来——比如下单或用户注册,而不是抽象的技术操作。这种方法提升了代码可读性,简化了测试,并在复杂后端系统中有效扩展项目。

UseCase 是什么?为什么值得使用?

UseCase 在应用层代表一个完整的业务场景。它协调一系列步骤,将业务规则委托给领域服务和实体。例如,不是低级的“写入数据库”操作,而是描述“用户下单”。这样,随着项目增长,业务逻辑不会散布在控制器和服务中。没有明确的场景边界,代码会变得难以理解和维护。

UseCase 的核心优势:

Google AdInline article slot
  • 清晰结构:系统由明确的业务能力定义,而不是模糊的 Service 或 Manager 等抽象概念。
  • 更好可读性:架构镜像业务逻辑,便于回答“这个应用能做什么?”
  • 更易测试:场景级别的集成测试验证完整用户路径,而不破坏业务流程。

局限性与最佳实践

避免在 UseCase 中调用另一个 UseCase,以防隐藏依赖并将其转为可复用逻辑块。在复杂场景中,只要无循环依赖且场景保持明确即可。对于可复用逻辑,提取到专用的服务或领域组件中,可跨多个 UseCase 使用。

UseCase 的调用位置:

  • 控制器
  • 控制台命令
  • 任务队列
  • 事件监听器

UseCase 作为应用业务逻辑的入口点,调解外部交互与系统内部。

Google AdInline article slot

仅用 Service 为什么不够用

Service 在简单项目中管用,但业务逻辑膨胀时问题就来了。原本简单的“保存订单到数据库”,会演变为检查库存、应用折扣、扣减余额、发送通知等。这会制造“上帝类”——依赖、方法和副作用臃肿的类,导致代码难读难测。

UseCase 遵循简单规则:一个 UseCase = 一个业务流程。重复逻辑放入共享服务或领域组件,随着复杂度增加保持代码可控。

PHP 和 Laravel 中的 UseCase 结构

典型的 UseCase 包含三部分:

Google AdInline article slot
  • Handler:协调场景步骤和条件,不含领域业务逻辑。
  • DataInput:输入数据对象,无逻辑——仅类型化、优选不可变数据。
  • DataOutput:结果对象定义清晰契约(如 DTO)。

示例:创建订单

class StoreOrderDataInput
{
    public function __construct(
        public readonly int $userId,
        public readonly array $items,
    ) {}
}

class StoreOrderDataOutput
{
    public function __construct(
        public readonly int $orderId,
        public readonly string $status,
    ) {}
}

class StoreOrderHandler
{
    public function __construct(
        private OrderRepository $orders
    ) {}

    public function handle(StoreOrderDataInput $input): StoreOrderDataOutput
    {
        $order = new Order(
            userId: $input->userId,
        );

        $orderSaved = $this->orders->save($order);

        return new StoreOrderDataOutput(
            orderId: $orderSaved->id,
            status: $orderSaved->status
        );
    }
}

项目中组织和测试 UseCase

在 Laravel 中,将 UseCase 存放在类似 app/UseCases/V1 的结构下,带版本控制以安全变更契约。子目录回答“系统能做什么?”——如 StoreOrderCancelOrderRegisterUser。这镜像业务能力,而非技术细节。

测试 UseCase 简单,使用场景级集成测试。StoreOrderHandler 示例测试:

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class StoreOrderFeatureTest extends TestCase
{
    use RefreshDatabase;

    public function testUserCanCreateOrder(): void
    {
        $user = User::factory()->create();

        $payload = [
            'items' => [
                ['id' => 10, 'qty' => 2],
                ['id' => 15, 'qty' => 1],
            ],
        ];

        $response = $this->actingAs($user)
            ->postJson('/api/orders', $payload);

        $response->assertStatus(200);
        $response->assertJsonStructure([
            'orderId',
            'status'
        ]);
        $this->assertDatabaseHas('orders', [
            'user_id' => $user->id
        ]);
    }
}

核心要点

  • UseCase 定义业务场景,而非技术操作,实现更清晰的应用结构。
  • 解决大型项目逻辑散乱,保持代码可维护。
  • 一个 UseCase = 一个业务流程,共享逻辑放服务中。
  • 测试更简单,通过覆盖完整场景的集成测试。
  • 项目结构反映业务特性,加速新人上手。

— Editorial Team

Advertisement 728x90

继续阅读