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:
| Folder | Responsibility | 
|---|---|
| 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-datawhen 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
| Type | Naming | Example | 
|---|---|---|
| Action | {Verb}{Noun}Action | CreateInvoiceAction | 
| Service | {Noun}Service | ClientService | 
| DTO | {Noun}Data | SubscriberData | 
| ViewModel | Get{Something}ViewModel | GetDashboardViewModel | 
| Value Object | {Concept} | Percent, Money | 
| State | {State}{Entity}Status | PaidOrderStatus | 
π§° 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.
