Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Verbs Laravel Package

hirethunk/verbs

Verbs is a Laravel-friendly event sourcing package for PHP artisans that keeps the benefits of event sourcing while cutting boilerplate and jargon. Model behavior as verbs, record events, and build projections with a clean, approachable API.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require hirethunk/verbs
    php artisan verbs:install
    

    This publishes the config and migrations. Run migrations afterward:

    php artisan migrate
    
  2. Define a State: Create a state class (e.g., app/Domain/User/States/UserState.php):

    namespace App\Domain\User\States;
    
    use HireThunk\Verbs\State;
    
    class UserState extends State
    {
        public string $name;
        public int $age;
    }
    
  3. Define a Verb (Event): Create a verb class (e.g., app/Domain/User/Verbs/UpdateUserName.php):

    namespace App\Domain\User\Verbs;
    
    use HireThunk\Verbs\Verb;
    
    class UpdateUserName extends Verb
    {
        public function __construct(public string $newName) {}
    }
    
  4. Register the State and Verb: In config/verbs.php, add your state and verb to the states and verbs arrays:

    'states' => [
        \App\Domain\User\States\UserState::class,
    ],
    'verbs' => [
        \App\Domain\User\Verbs\UpdateUserName::class,
    ],
    
  5. First Use Case: Fire a verb in a controller or service:

    use App\Domain\User\Verbs\UpdateUserName;
    use HireThunk\Verbs\Facades\Verbs;
    
    public function updateName(string $newName)
    {
        Verbs::fire(new UpdateUserName($newName));
    }
    

Where to Look First

  • Documentation for conceptual overviews, API references, and examples.
  • app/Domain/ for organizing your states and verbs by domain.
  • config/verbs.php for configuration options like event store, ID types, and metadata.
  • database/migrations/ for customizing the event store schema.

Implementation Patterns

Core Workflow: Event Sourcing with Verbs

  1. Define States and Verbs:

    • States represent the current state of an entity (e.g., UserState).
    • Verbs represent actions that transition states (e.g., UpdateUserName, DeleteUser).
  2. Fire Verbs: Use the Verbs facade to fire verbs:

    Verbs::fire(new UpdateUserName('Alice'));
    
    • Verbs are automatically persisted to the event store.
    • States are updated via apply() methods (see below).
  3. Apply Verbs to States: Override the apply() method in your state to handle verb logic:

    class UserState extends State
    {
        public function apply(UpdateUserName $verb)
        {
            $this->name = $verb->newName;
        }
    }
    
  4. Replay Events: Verbs handles replaying events to reconstruct state:

    $userState = Verbs::replay(UserState::class, $userId);
    
  5. Listen to Events: Use Laravel's event listeners or Verbs' listen() method:

    Verbs::listen(UpdateUserName::class, function (UpdateUserName $verb) {
        // Side effects (e.g., notifications, analytics)
    });
    

Integration Patterns

1. Domain-Driven Design (DDD) Integration

  • Aggregate Roots: Use states as aggregate roots (e.g., OrderState).
  • Repositories: Create repositories to fetch and persist states:
    class UserRepository
    {
        public function find(int $id): ?UserState
        {
            return Verbs::replay(UserState::class, $id);
        }
    }
    

2. Livewire Integration

Commit verbs just before rendering:

use HireThunk\Verbs\Facades\Verbs;
use HireThunk\Verbs\Livewire\CommitVerbs;

public function mount()
{
    $this->commitVerbs = new CommitVerbs();
}

public function updatedName()
{
    Verbs::fire(new UpdateUserName($this->name));
}

3. Testing

  • Replay Events in Tests:
    public function test_user_update()
    {
        Verbs::replay(UserState::class, $userId);
        $this->assertEquals('Alice', $userState->name);
    }
    
  • Mock Verbs: Use Verbs::fake() to mock verb firing:
    Verbs::fake();
    Verbs::shouldFire(UpdateUserName::class)->once();
    

4. Custom Factories

Extend the default state factory for domain-specific logic:

namespace App\Domain\User\Factories;

use App\Domain\User\States\UserState;
use HireThunk\Verbs\StateFactory;

class UserStateFactory extends StateFactory
{
    public function for(UserState $state, array $attributes = [])
    {
        return $state->replicate()->fill($attributes);
    }
}

Register in config/verbs.php:

'factories' => [
    UserState::class => \App\Domain\User\Factories\UserStateFactory::class,
],

5. Metadata and Snapshots

  • Add Metadata:
    Verbs::fire(new UpdateUserName('Alice'), [
        'source' => 'admin_panel',
        'user_id' => auth()->id(),
    ]);
    
  • Snapshots: Enable snapshots in config/verbs.php:
    'snapshots' => true,
    
    Snapshots store the current state after every event for faster reconstruction.

6. Concurrency Control

Use optimistic_lock or pessimistic_lock in config/verbs.php:

'concurrency' => [
    'guard' => 'optimistic_lock',
    'column' => 'version',
],

Common Use Cases

1. Command Handling

Map HTTP requests to verbs in controllers:

public function update(Request $request, int $userId)
{
    Verbs::fire(new UpdateUserName($request->name));
    return response()->json(['status' => 'updated']);
}

2. Background Jobs

Fire verbs in queue jobs:

class SendWelcomeEmail implements ShouldQueue
{
    public function handle()
    {
        Verbs::fire(new UserRegistered($userId));
    }
}

3. API Versioning

Use verbs to enforce API contracts:

public function createOrder(Request $request)
{
    Verbs::fire(new CreateOrder(
        $request->user_id,
        $request->items,
        $request->shipping_address
    ));
}

4. Audit Logging

Listen to verbs to log changes:

Verbs::listen(function ($verb) {
    \Log::info('Verb fired', ['verb' => $verb::class, 'data' => $verb]);
});

Gotchas and Tips

Pitfalls

  1. State Reconstruction Issues:

    • Problem: States fail to reconstruct during replay.
    • Cause: Missing apply() methods or incorrect state property types.
    • Fix: Ensure all verbs have corresponding apply() methods in the state. Use php artisan verbs:generate:state to scaffold states.
    • Debug: Check the verbs_events table for missing events or malformed data.
  2. Circular Dependencies:

    • Problem: States or verbs reference each other circularly, causing serialization errors.
    • Fix: Avoid circular references in state properties. Use lazy loading or IDs instead of full objects.
  3. Concurrency Conflicts:

    • Problem: Optimistic lock failures in high-contention scenarios.
    • Fix: Use pessimistic_lock for critical sections or implement retry logic:
      try {
          Verbs::fire($verb);
      } catch (\HireThunk\Verbs\Exceptions\ConcurrencyException $e) {
          // Retry or handle conflict
      }
      
  4. Event Store Schema Mismatches:

    • Problem: Custom migrations conflict with Verbs' default schema.
    • Fix: Extend the default migration or use a custom event store (e.g., PostgreSQL JSONB).
  5. Livewire State Persistence:

    • Problem: Verbs fired in Livewire components don’t persist.
    • Fix: Ensure CommitVerbs middleware is registered and the component uses wire:model.live for reactive updates.
  6. Metadata Serialization:

    • Problem: Complex metadata (e.g., objects, closures) fails to serialize.
    • Fix: Use JSON-serializable data or flatten objects
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope