← Back to writing
4 min read

Event-Driven Evolution with AI as My Partner in Design

Laravel Php Learning

Naomi Gotts (Senior Software Engineer, Tillo) works at a fintech company focused on gift card infrastructure. She has spoken previously on TDD at PHP Sussex and the International PHP Conference. This talk appears to be her first on event-driven architecture and AI-assisted design.

Document and define all business domain keywords and processes

One of the central points from the talk: before building event-driven systems, or working with AI on design, every significant term in a project's business logic needs to be named, agreed upon, and recorded. This is the concept of a ubiquitous language from Domain-Driven Design (DDD).

The problem without it: developers use their own technical vocabulary while business stakeholders use domain-specific terms, and the two do not match. The terminology in day-to-day conversations becomes disconnected from the terminology in code. This gap produces misunderstandings in requirements and inconsistencies in implementation.

The practice: during discovery and design, work with domain experts to identify every significant concept (entities, actions, states, and transitions) and settle on a single name for each. Those names should be used consistently in code, documentation, tests, conversations, and prompts given to AI tools.

For event-driven systems specifically, naming matters more than usual because events represent things that have happened in the domain ("OrderShipped", "PaymentDeclined", "AccountCreated"). Events named with domain language are self-documenting; events named with technical language ("RecordUpdated", "StatusChanged") are not.

References:

Set up a central area for domain knowledge

The practical implementation of the above: a single, maintained location where the domain language, bounded contexts, event names, and process definitions live. All developers, and any AI tooling used on the project, reference this same source.

For AI-assisted development, this directly addresses the "implicit knowledge problem": the fact that AI coding tools have no project memory between sessions and no awareness of business-specific context. Every session starts blank. Without a documented domain knowledge source, the AI will pattern-match against generic conventions rather than project-specific ones. With it, the AI can be given accurate context from the start, and the output reflects the actual domain.

Concretely, this could be:

A CLAUDE.md (or AGENTS.md) file at the project root documenting domain terminology, bounded contexts, key event names, and the naming conventions used across the codebase. AI coding tools like Claude Code and Cursor read this file at the start of each session.

A dedicated directory (e.g. docs/domain/) containing more detailed documentation covering domain glossaries, event catalogues, and bounded context maps that can be referenced explicitly when working on specific areas.

The principle is the same regardless of format: one source of truth for domain knowledge that is kept in version control, updated by the team when the domain evolves, and accessible to both humans and AI tools.

This connects directly to what Aaron Francis discussed at the same conference - the importance of providing AI with structured project context so it can operate within the right boundaries.

References:

Event-driven architecture in Laravel

Laravel has a built-in event system that implements the observer/pub-sub pattern. Events are dispatched when something meaningful occurs; listeners respond to those events and handle side effects.

// An event representing something that happened in the domain
class OrderShipped
{
    use Dispatchable, SerializesModels;

    public function __construct(public Order $order) {}
}

// A listener that reacts to it
class SendShipmentNotification implements ShouldQueue
{
    public function handle(OrderShipped $event): void
    {
        Mail::to($event->order->customer_email)
            ->send(new ShipmentNotification($event->order));
    }
}

Listeners can implement ShouldQueue to run asynchronously, decoupling response time from the original action. Multiple listeners can respond to the same event independently.

The key benefit is decoupling: the code that ships an order does not need to know about email notifications, inventory updates, or analytics; it simply dispatches an event and those concerns listen for it.

For more complex use cases, event sourcing stores each event as the source of truth rather than current state. Spatie's laravel-event-sourcing package is the standard Laravel implementation. However, Naomi and her team opted for a more custom solution within Tillo, although the reasoning was not clear.

References:


Notes from Laravel Live UK 2026.