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 PHP event sourcing package for Laravel artisans. It delivers the benefits of event sourcing while minimizing boilerplate and jargon, making it simpler to model behavior and build systems by thinking in actions (verbs) instead of nouns.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture Fit

  • Event Sourcing Paradigm: Verbs aligns perfectly with event-driven architectures, particularly for systems requiring auditability, replayability, or complex state transitions. Its "think in verbs, not nouns" philosophy reduces boilerplate while enforcing clarity in event-state relationships.
  • Laravel Synergy: Designed for Laravel, it integrates seamlessly with Eloquent, middleware, and service providers. The package leverages Laravel’s conventions (e.g., #[StateId], handle() methods) to minimize friction.
  • State Management: The separation of events (what happened) and states (current reality) mirrors CQRS patterns, making it ideal for domains with high write complexity (e.g., financial systems, workflows).
  • Metadata Flexibility: Dynamic metadata injection (e.g., team_id, user_preferences) enables cross-cutting concerns without polluting event classes, a common pain point in event sourcing.

Integration Feasibility

  • Low-Ceremony Adoption: The verbs:event and verbs:state Artisan commands scaffold boilerplate, reducing initial setup time. Attributes like #[AppliesToState] and #[StateId] automate state-event binding.
  • Database Agnosticism: While migrations are required, Verbs abstracts storage details (e.g., event tables, state snapshots) behind a clean API. Supports PostgreSQL/MySQL out-of-the-box.
  • Hybrid Models: States can coexist with Eloquent models, allowing gradual migration from traditional ORM patterns. The handle() method bridges events to model persistence.
  • Legacy Compatibility: The Listen attribute (though undocumented in the latest docs) suggests potential for reactive programming with existing Laravel events.

Technical Risk

  • Learning Curve: Event sourcing introduces new concepts (e.g., apply() vs. handle(), state validation). Teams unfamiliar with CQRS may struggle with:
    • Event Replay: The #[Once] attribute and unlessReplaying guard against idempotency issues, but replay logic must be explicitly designed.
    • State Corruption: Invalid state transitions (e.g., validate() failures) can break invariants. Verbs lacks built-in rollback mechanisms for failed events.
  • Performance Overheads:
    • Event Storage: Storing every event (vs. snapshotting) can bloat databases. Verbs requires explicit configuration for snapshot policies.
    • State Reconstruction: Loading historical states for replay or queries may be slow without indexing (e.g., state_id + event_type composite keys).
  • Tooling Gaps:
    • No built-in event viewer or debugging tools (e.g., "why did this state change?").
    • Limited support for distributed transactions (e.g., sagas) out-of-the-box.
  • PHP-Specific Risks:
    • Attribute reflection (used for #[StateId]) may have edge cases in older PHP versions (though Laravel 10+ mitigates this).
    • Serialization of complex state objects (e.g., closures, resources) could fail silently.

Key Questions

  1. Domain Suitability:
    • Is your system write-heavy with complex state transitions (e.g., order processing, multi-step workflows)? If not, traditional ORM may suffice.
    • Do you need temporal queries (e.g., "show me all events for user X in 2023")? Verbs excels here but requires custom projections.
  2. Team Readiness:
    • Can your team adopt event-driven thinking? Verbs abstracts complexity but doesn’t eliminate the need to design events intentionally.
    • Are developers comfortable with attribute-based configuration (e.g., #[AppliesToChildState])?
  3. Operational Trade-offs:
    • Can you tolerate the storage cost of event sourcing (e.g., 10x more rows than a traditional DB)?
    • How will you handle event replay during deployments or migrations? Verbs provides tools but not automation.
  4. Extensibility:
    • Do you need custom projections (e.g., materialized views)? Verbs lacks a built-in query builder; you’d need to implement listeners or use Laravel’s query caching.
    • Will third-party packages (e.g., payment gateways) integrate easily? The metadata system helps, but event schemas must be designed collaboratively.

Integration Approach

Stack Fit

  • Laravel Ecosystem:
    • Eloquent: States can extend State (a lightweight alternative to Eloquent) or use Eloquent models directly. Verbs provides a VerbsHistory trait for event history on models.
    • Middleware: Metadata injection (e.g., team_id) fits naturally into Laravel’s middleware pipeline.
    • Queues: Events can be fired asynchronously via Laravel’s queue system, with handle() methods running in workers.
    • Livewire/Inertia: Real-time state updates can trigger UI changes via Laravel Echo or Livewire events.
  • PHP Version: Requires PHP 8.1+ (for attributes and named arguments), aligning with Laravel 10+.
  • Database: Optimized for PostgreSQL/MySQL. For other databases (e.g., SQLite), ensure UUID/snowflake support for state_id.
  • Testing: Works with Laravel’s testing tools (e.g., Verbs::fake() for mocking events in unit tests).

Migration Path

  1. Pilot Phase:
    • Start with a single bounded context (e.g., "subscriptions" or "orders") to isolate risks.
    • Use Verbs alongside existing ORM for hybrid writes (e.g., handle() updates Eloquent, while state tracks domain invariants).
    • Example: Replace a monolithic User model with:
      • UserState (for domain logic, e.g., trial_expired_at).
      • User Eloquent model (for queries, e.g., users.where('email', ...)).
  2. Incremental Adoption:
    • Step 1: Replace CRUD operations with events for new features (e.g., "user signup" → UserRegistered event).
    • Step 2: Add state validation (e.g., validate() in UserRegistered to prevent duplicate emails).
    • Step 3: Migrate read models to projections (e.g., a UserProfile view built from events).
  3. Tooling Setup:
    • Publish migrations early: php artisan vendor:publish --tag=verbs-migrations.
    • Configure metadata providers in a ServiceProvider (e.g., inject tenant_id from middleware).
    • Set up event listeners for cross-cutting concerns (e.g., logging, analytics).

Compatibility

  • Existing Code:
    • Controllers: Replace direct model saves with event firing (e.g., User::create()UserRegistered::fire()).
    • Jobs/Commands: Convert synchronous logic to event handlers (e.g., handle() methods).
    • APIs: Use events to decouple services (e.g., "payment processed" → PaymentProcessed event triggers notifications).
  • Third-Party Packages:
    • Payment Gateways: Wrap SDK calls in events (e.g., ChargeCreated::fire()) to ensure idempotency.
    • Auth: Use metadata to attach user_id to events (e.g., Verbs::createMetadataUsing(fn () => ['user_id' => auth()->id()])).
  • Legacy Systems:
    • Use the Listen attribute to react to existing Laravel events (e.g., #[Listen(\Illuminate\Auth\Events\Registered::class)]).

Sequencing

  1. Schema First:
    • Design events and states before writing handlers. Use the "event storming" technique to identify domain verbs.
    • Example: For an e-commerce system, events might include OrderPlaced, PaymentAuthorized, InventoryReserved.
  2. State Design:
    • States should be minimal: Only store data needed for validation or future events (e.g., last_order_at).
    • Use #[AppliesToChildState] for hierarchical data (e.g., OrderStateOrderItemState).
  3. Event Lifecycle:
    • Fire: Use Event::fire() in controllers, jobs, or middleware.
    • Validate: Implement validate(State) to enforce invariants.
    • Apply: Use apply(State) to update state immutably.
    • Handle: Use handle() for side effects (e.g., sending emails, updating external systems).
  4. Testing Order:
    • Write state tests first (e.g., "given state X, event Y should transition to state Z").
    • Then write event tests (e.g., "firing X triggers side effect Y").
    • Finally, test end-to-end flows (e.g., "user checkout → order confirmation").

Operational Impact

Maintenance

  • Boilerplate Reduction:
    • Attributes (#[StateId], #[AppliesToState]) reduce repetitive code (e.g., no need for manual state lookup logic).
    • Artisan commands (verbs:event, verbs:state) enforce consistency.
  • Schema Evolution:

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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport
twbs/bootstrap4
php-http/client-implementation
phpcr/phpcr-implementation
cucumber/gherkin-monorepo
haydenpierce/class-finder
psr/simple-cache-implementation
uri-template/tests