MCP Agents Guide: Laravel & Domain Driven Design (DDD) Rules

Agents.md β€” Domain-Driven Development Guide for Laravel

This document defines how engineers should design and implement agents (domains, actions, and data flows) following Domain-Driven Design (DDD) principles within a Laravel application.

Copy this into your project’s docs/agents/domain-driven-design.md and adapt as needed.


πŸ—οΈ 1. Purpose

An agent represents a coherent business capability β€” for example, Subscriber, Broadcast, Invoice, or Automation.
Each agent encapsulates all logic, data, and rules that belong to its business area.

πŸ—£οΈ Goal: Align code with business language β€” your code should read like the product owner’s vocabulary.


🧩 2. Agent Structure

Every agent lives under app/Domain/{AgentName} and contains:

Domain/
 └── AgentName/
      β”œβ”€β”€ Actions/
      β”œβ”€β”€ DTOs/
      β”œβ”€β”€ Models/
      β”œβ”€β”€ Services/
      β”œβ”€β”€ ViewModels/
      β”œβ”€β”€ ValueObjects/
      └── Support/

Each folder has a clear purpose:

FolderResponsibility
Actions/Encapsulate one user story or business command
DTOs/Typed data objects for communication between layers
Models/Eloquent models representing entities
Services/Business logic or external API integration
ViewModels/Read/query models that prepare response data
ValueObjects/Immutable representations of scalar domain values
Support/Shared helpers, enums, states, or transitions

βš™οΈ 3. Core Rules

1. Business-First Naming

Use the same language the business uses.
Rename technical terms:

User β†’ Customer
Job β†’ Broadcast
Task β†’ AutomationStep

2. Value Objects (VO)

  • Represent immutable, ID-less business values.
  • Contain small, focused logic (formatting, normalization).
  • Never modify in place; always return new instances.
final class Percent
{
    public function __construct(public readonly float $value) {}
    public function formatted(): string => number_format($this->value * 100, 2).'%';
}

πŸ“œ Rule: β€œIf it represents a value, not an entity, make it a Value Object.”


3. Data Transfer Objects (DTO)

  • Structure untyped data (array $data) into objects.
  • Move data safely between layers (Controllers β†’ Services β†’ Actions).
  • Use spatie/laravel-data when possible.
final class CourseData extends Data
{
    public function __construct(
        public readonly string $title,
        public readonly string $description,
        /** @var Collection<LessonData> */
        public readonly Collection $lessons
    ) {}
}

πŸ“œ Rule: β€œAll communication between layers happens through DTOs.”


4. Actions

  • Describe a single user story.
  • Contain one public execute() or __invoke() method.
  • Return a DTO or Value Object, not raw models.
final class CreateSubscriberAction
{
    public static function execute(SubscriberData $data): SubscriberData
    {
        $subscriber = Subscriber::create($data->all());

        return SubscriberData::from($subscriber);
    }
}

πŸ“œ Rule: β€œEach action = one business operation.”


5. Services

  • Implement reusable domain logic or integrate with external APIs.
  • Avoid overlapping with Actions β€” Services should not represent user stories.
final class ClientService
{
    public function getById(int $id): ClientData
    {
        return ClientData::from(Client::findOrFail($id));
    }
}

πŸ“œ Rule: β€œUse Services for cross-domain or external concerns.”


6. ViewModels

  • Represent read-side data for views or APIs.
  • Contain only query logic β€” no writes.
  • Return arrays or DTOs.
final class GetDashboardViewModel extends ViewModel
{
    public function newSubscribersCount(): NewSubscribersCountData
    {
        return new NewSubscribersCountData(
            today: Subscriber::today()->count(),
            total: Subscriber::count()
        );
    }
}

πŸ“œ Rule: β€œEvery screen or API response = one ViewModel.”


7. CQRS

Separate writes (commands) and reads (queries) clearly:

  • Commands β†’ Actions
  • Queries β†’ ViewModels

πŸ“œ Rule: β€œNever mix write and read responsibilities in one class.”


8. States & Transitions

Represent lifecycle logic explicitly.

abstract class OrderStatus {
    abstract public function canBeChanged(): bool;
}

final class PaidOrderStatus extends OrderStatus {
    public function canBeChanged(): bool => false;
}

πŸ“œ Rule: β€œIf you find yourself comparing strings for state, use state classes.”


🧱 4. Boundaries Between Agents

  • Never pass Eloquent models across domains.
  • Expose DTOs or Service APIs, not models.
  • Avoid foreign keys that cross domain boundaries unless absolutely required.
  • Keep each agent independently testable and deployable.

πŸ“œ Rule: β€œDomains communicate through DTOs, not relationships.”


🧭 5. Recommended File Patterns

TypeNamingExample
Action{Verb}{Noun}ActionCreateInvoiceAction
Service{Noun}ServiceClientService
DTO{Noun}DataSubscriberData
ViewModelGet{Something}ViewModelGetDashboardViewModel
Value Object{Concept}Percent, Money
State{State}{Entity}StatusPaidOrderStatus

🧰 6. Practical Guidelines

  • Keep models thin, actions focused, DTOs explicit.
  • Avoid static helpers and global state.
  • Keep business rules in the domain, not the controller.
  • Prefer expressiveness over brevity.
  • Each domain should feel like a mini-app.

🧩 7. Example Agent Layout

app/Domain/Subscriber/
 β”œβ”€β”€ Actions/
 β”‚    β”œβ”€β”€ CreateSubscriberAction.php
 β”‚    └── UpdateSubscriberAction.php
 β”œβ”€β”€ DTOs/
 β”‚    └── SubscriberData.php
 β”œβ”€β”€ Models/
 β”‚    └── Subscriber.php
 β”œβ”€β”€ Services/
 β”‚    └── ImportSubscriberService.php
 β”œβ”€β”€ ViewModels/
 β”‚    └── GetSubscriberStatsViewModel.php
 β”œβ”€β”€ ValueObjects/
 β”‚    └── EmailAddress.php
 └── Support/
      └── States/
          └── SubscriptionStatus.php

🧭 8. Checklist Before Shipping

  • Every public method maps to a business action
  • DTOs replace arrays in all inter-layer communication
  • No direct cross-domain Eloquent relationships
  • Domain folder names match business language
  • Value Objects cover reusable primitives
  • Tests target actions and view-models
  • Read/Write responsibilities are clearly separated

βœ… TL;DR β€” Agent Design Mantra

Speak the business language

Model reality, not databases.

Each action tells a story.

Each domain stands alone.

MCP Agents Guide: Laravel & Domain Driven Design (DDD) Rules - Rob Mellett