Powrót do strony głównej

UseCase w rozwoju backendu: strukturyzacja logiki biznesowej

Artykuł wyjaśnia wzorzec UseCase do opisywania scenariuszy biznesowych w aplikacjach backendowych. Omówiono strukturę, zalety, ograniczenia i przykłady implementacji w PHP i Laravel, w tym testowanie i organizację kodu.

UseCase: jak opisywać procesy biznesowe w kodzie dla programistów
Advertisement 728x90

Wzorzec UseCase: strukturyzacja logiki biznesowej w backend-developmentie

UseCase to architektoniczny wzorzec opisujący scenariusze biznesowe w aplikacjach. Pomaga organizować kod wokół konkretnych działań systemu, takich jak tworzenie zamówienia czy rejestracja użytkownika, zamiast abstrakcyjnych operacji technicznych. To podejście poprawia czytelność, ułatwia testowanie i skalowanie projektów, zwłaszcza w złożonych systemach backendowych.

Czym jest UseCase i dlaczego jest potrzebny

UseCase reprezentuje kompletny scenariusz biznesowy na poziomie aplikacji (Warstwa Aplikacji). Zarządza sekwencją kroków w scenariuszu, delegując reguły biznesowe do serwisów domenowych i encji. Na przykład, zamiast technicznej operacji "zapisz wiersz do bazy danych", UseCase opisuje działanie "użytkownik składa zamówienie". Rozwiązuje to problem rozpraszania logiki po kontrolerach i serwisach, który pojawia się wraz z rozwojem projektu. Bez wyraźnych granic scenariuszy kod staje się trudny do zrozumienia i utrzymania.

Kluczowe zalety UseCase:

Google AdInline article slot
  • Jasna struktura: System jest opisany poprzez wyraźne możliwości biznesowe, a nie abstrakcyjne encje takie jak Service czy Manager.
  • Lepsza czytelność: Architektura staje się zrozumiała na poziomie logiki biznesowej, co ułatwia odpowiedź na pytanie "Co potrafi aplikacja?".
  • Łatwość testowania: Testy integracyjne na poziomie scenariusza pozwalają sprawdzić całą ścieżkę użytkownika bez ryzyka naruszenia procesu biznesowego.

Ograniczenia i zalecenia dotyczące użycia

W większości przypadków nie zaleca się wywoływania jednego UseCase z drugiego, aby uniknąć ukrytych zależności i przekształcenia UseCase w wielokrotnie używane bloki logiki. Jednak w złożonych scenariuszach jest to dopuszczalne przy braku cyklicznych zależności i zachowaniu jasności scenariuszy. Jeśli pojawi się potrzeba ponownego użycia logiki, warto ją wyodrębnić do oddzielnych serwisów lub komponentów domenowych, które mogą być później stosowane w różnych UseCase.

Gdzie są wywoływane UseCase:

  • Controller
  • Console Command
  • Job
  • Event Listener

UseCase służy jako punkt wejścia do logiki biznesowej aplikacji, przez który świat zewnętrzny wchodzi w interakcję z systemem.

Google AdInline article slot

Dlaczego warstwa Service może być niewystarczająca

Na początku projektu warstwa Service często wystarcza, ale wraz z rozwojem logiki biznesowej pojawiają się problemy. Na przykład, tworzenie zamówienia z prostej operacji zapisu do bazy może wiązać się z dodatkowymi krokami: sprawdzanie zapasów, stosowanie rabatów, pobieranie salda, wysyłanie powiadomień. Prowadzi to do powstania klas-bóstw (God-object) z wieloma zależnościami, metodami i efektami ubocznymi, co utrudnia czytanie kodu i pisanie testów.

UseCase oferuje proste zasady: jeden UseCase = jeden proces biznesowy. Powtarzalna logika jest wyodrębniana do oddzielnych serwisów i komponentów domenowych, co zachowuje sterowalność kodu nawet przy wzroście złożoności.

Struktura UseCase na przykładzie PHP i Laravel

Typowa struktura UseCase obejmuje trzy komponenty:

Google AdInline article slot
  • Handler: Zarządza sekwencją wykonania scenariusza, zawiera warunki kroków, ale nie domenową logikę biznesową.
  • DataInput: Obiekt danych wejściowych bez logiki, tylko typizowane dane, najlepiej niezmienne.
  • DataOutput: Obiekt wyniku wykonania, formułujący wyraźny kontrakt (np. DTO).

Przykład tworzenia zamówienia:

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
        );
    }
}

Organizacja i testowanie UseCase w projektach

W Laravel zaleca się przechowywanie UseCase w strukturze, np. app/UseCases/V1, z wersjonowaniem dla bezpiecznej zmiany kontraktów. Katalogi wewnątrz odpowiadają na pytanie "Co potrafi system?", np. StoreOrder, CancelOrder, RegisterUser. Odzwierciedla to możliwości biznesowe systemu, a nie szczegóły techniczne.

Testowanie UseCase upraszcza się dzięki testom integracyjnym na poziomie scenariusza. Przykład testu dla 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
        ]);
    }
}

Co jest ważne

  • UseCase opisuje scenariusze biznesowe, a nie operacje techniczne, poprawiając strukturę aplikacji.
  • Wzorzec rozwiązuje problem rozpraszania logiki w dużych projektach, czyniąc kod bardziej sterowalnym.
  • Jeden UseCase odpowiada jednemu procesowi biznesowemu, z wyodrębnieniem powtarzalnej logiki do oddzielnych serwisów.
  • Testowanie jest ułatwione dzięki testom integracyjnym, które sprawdzają cały scenariusz.
  • Struktura projektu odzwierciedla możliwości biznesowe, co przyspiesza włączanie nowych deweloperów.

— Editorial Team

Advertisement 728x90

Czytaj dalej