### Usage of CreatePostCommand Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Example of how to instantiate and execute a `CreatePostCommand` with data typically sourced from a request. ```php $post = (new CreatePostCommand( authorId: $request->user()->id, title: $request->string('title'), body: $request->string('body'), ))->execute(); ``` -------------------------------- ### Update Command Constructor (Before Toolkit) Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Example of a Command constructor before integrating the toolkit, including the app()->call to boot method. ```php public function __construct( public readonly string $email, ) { app()->call([$this, 'boot']); // remove this line } public function boot(WelcomeEmailSender $welcomeEmail): void { $this->welcomeEmail = $welcomeEmail; // no changes } ``` -------------------------------- ### Create and Publish Post Action Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt This Action handles a multi-step process: getting an author, creating a post, publishing it, and notifying followers. Dependencies are wired in boot(). ```php class CreateAndPublishPostAction { private NotificationService $notifications; public function __construct( public readonly int $authorId, public readonly string $title, public readonly string $body, ) { app()->call([$this, 'boot']); } public function boot(NotificationService $notifications): void { $this->notifications = $notifications; } public function execute(): Post { $author = (new GetAuthorQuery(id: $this->authorId))->execute(); $post = (new CreatePostCommand( authorId: $this->authorId, title: $this->title, body: $this->body, ))->execute(); (new PublishPostCommand(postId: $post->id))->execute(); $this->notifications->notifyFollowers($author, $post); return $post; } } ``` -------------------------------- ### Action Invocation with Middleware (After Toolkit) Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Example of chaining middleware before dispatching an Action using ::make() after integrating the toolkit. ```php $user = CreateUserCommand::make(name: $this->name, email: $this->email, password: $this->password) ->middleware(TransactionMiddleware::class) ->dispatch(); ``` -------------------------------- ### Install Utilitarian Laravel Toolkit Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Command to install the utilitarian/laravel-toolkit package using Composer. ```bash composer require utilitarian/laravel-toolkit ``` -------------------------------- ### Migrate to Utilitarian Laravel Toolkit Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Install the toolkit and update constructor and call sites. Remove `app()->call` from constructors and use `.make()` and `.dispatch()` for actions. ```php // Step 1: install // composer require utilitarian/laravel-toolkit // Step 2: remove app()->call([$this, 'boot']) from constructors // Before: public function __construct(public readonly string $title, public readonly string $body) { app()->call([$this, 'boot']); // ← remove this line only } // After: public function __construct(public readonly string $title, public readonly string $body) {} // boot() and execute() are completely unchanged // Step 3: update call sites // Before: $post = (new CreateAndPublishPostAction( authorId: $request->user()->id, title: $request->string('title'), body: $request->string('body'), ))->execute(); // After: $post = CreateAndPublishPostAction::make( authorId: $request->user()->id, title: $request->string('title'), body: $request->string('body'), )->dispatch(); // Toolkit-only: chaining middleware before dispatch $user = CreateUserCommand::make(name: 'Alice', email: 'alice@example.com', password: 'secret') ->middleware(TransactionMiddleware::class) ->dispatch(); ``` -------------------------------- ### Update Command Constructor (After Toolkit) Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Example of a Command constructor after integrating the toolkit, with app()->call removed from the constructor. ```php public function __construct( public readonly string $email, ) {} public function boot(WelcomeEmailSender $welcomeEmail): void { $this->welcomeEmail = $welcomeEmail; } ``` -------------------------------- ### Directory Structure Example Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Group code by feature/domain, not by CRUD operation. Mirror structure across Actions, Commands, and Queries. Infrastructure adapters lead the path. ```php app/ ├── Actions/ │ ├── Auth/ │ │ ├── RegisterUserAction.php │ │ └── LoginUserAction.php │ ├── Order/ │ │ ├── CreateOrderAction.php │ │ ├── ProcessPaymentAction.php │ │ └── CancelOrderAndCreateRefundAction.php # cross-domain → primary aggregate │ └── Post/ │ ├── CreateAndPublishPostAction.php │ └── UpdatePostAction.php ├── Commands/ │ ├── Auth/ │ │ └── CreateUserCommand.php │ ├── Order/ │ │ ├── CreateOrderCommand.php │ │ └── UpdateOrderStatusCommand.php │ └── Post/ │ ├── CreatePostCommand.php │ └── PublishPostCommand.php └── Queries/ ├── Order/ │ ├── GetOrderQuery.php │ └── GetUserOrdersQuery.php └── Post/ ├── GetPostsQuery.php └── GetPublishedPostsQuery.php ``` -------------------------------- ### Usage of Create and Publish Post Action from Route Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Example of how to use the CreateAndPublishPostAction within a route closure to handle incoming POST requests for creating posts. ```php // Usage from a route closure Route::post('/posts', function (Request $request) { $post = (new CreateAndPublishPostAction( authorId: $request->user()->id, title: $request->string('title'), body: $request->string('body'), ))->execute(); return response()->json($post, 201); })->name('posts.create'); ``` -------------------------------- ### Usage of Create User Command with Normalized Email Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Example showing the instantiation of CreateUserCommand with a raw email string, which is then automatically validated and normalized by the Email Value Object. ```php $user = (new CreateUserCommand( name: 'Alice', email: new Email(' Alice@Example.COM '), // → 'alice@example.com' password: 'secret', ))->execute(); ``` -------------------------------- ### Command Naming Convention Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Chapter 03 - Structure and Naming.md Commands should follow the pattern {Verb}{Noun}Command. Examples include CreatePostCommand, UpdateUserCommand, and DeleteOrderCommand. ```php CreatePostCommand UpdateUserCommand DeleteOrderCommand PublishPostCommand ProcessPaymentCommand ``` -------------------------------- ### Get Site Config Query (Request-Scoped Memoization) Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Demonstrates request-scoped memoization for repeated identical reads within a single request. This query caches the configuration to avoid redundant database calls. ```php // Request-scoped memoization (for repeated identical reads in one request) class GetSiteConfigQuery { private static array $cache = []; public function execute(): array { $key = 'site_config'; return self::$cache[$key] ??= Config::findOrFail('site')->toArray(); } } ``` -------------------------------- ### Get Published Posts Query (Eloquent Only) Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt A query that fetches published posts using only Eloquent, requiring no external dependencies or `boot()` method. Suitable for simple read operations. ```php // Query without boot — Eloquent only, no injection needed class GetPublishedPostsQuery { public function __construct( public readonly int $limit = 20, ) {} public function execute(): Collection { return Post::query() ->where('status', 'published') ->latest() ->limit($this->limit) ->get(); } } // Usage $posts = (new GetPublishedPostsQuery(limit: 10))->execute(); ``` -------------------------------- ### Query Naming Convention Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Chapter 03 - Structure and Naming.md Queries should follow the pattern {Get|Find}{Noun}Query or {Get|Find}{Adjective}{Noun}Query. Use 'Get' for collections/entities and 'Find' for searching. Add adjectives for filtering. ```php GetPostsQuery GetPostQuery GetPublishedPostsQuery FindUserByEmailQuery GetActiveSubscriptionsQuery ``` -------------------------------- ### Create Post Command (Full Version with Toolkit) Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Identical class body to the light version but uses the toolkit's `::make()->dispatch()` API for invocation. This version supports middleware like transactions. ```php // Full version (with toolkit): identical class body, different invocation $post = CreatePostCommand::make( authorId: $request->user()->id, title: $request->string('title'), body: $request->string('body'), )->dispatch(); // With middleware (toolkit only) $post = CreatePostCommand::make(authorId: 1, title: 'Hello', body: 'World') ->middleware(TransactionMiddleware::class) ->dispatch(); ``` -------------------------------- ### Create Post Command (Light Version) Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Encapsulates a single atomic state change for creating a post. The constructor takes immutable business parameters, and `boot()` resolves infrastructure dependencies. Use this version when not using the full toolkit. ```php class CreatePostCommand { private PostRepository $repo; public function __construct( public readonly int $authorId, public readonly string $title, public readonly string $body, ) { app()->call([$this, 'boot']); } public function boot(PostRepository $repo): void { $this->repo = $repo; } public function execute(): Post { return $this->repo->create([ 'author_id' => $this->authorId, 'title' => $this->title, 'body' => $this->body, ]); } } // Usage in a controller $post = (new CreatePostCommand( authorId: $request->user()->id, title: $request->string('title'), body: $request->string('body'), ))->execute(); ``` -------------------------------- ### Update Entry Point Invocation (After Toolkit) Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Demonstrates invoking an Action using ::make()->dispatch() at an entry point after integrating the toolkit. ```php // After: $post = CreateAndPublishPostAction::make( authorId: $request->user()->id, title: $request->string('title'), body: $request->string('body'), )->dispatch(); ``` -------------------------------- ### Update Entry Point Invocation (Before Toolkit) Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Shows direct instantiation and execution of an Action at an entry point before using the toolkit. ```php // Before: $post = (new CreateAndPublishPostAction( authorId: $request->user()->id, title: $request->string('title'), body: $request->string('body'), ))->execute(); ``` -------------------------------- ### RegisterUserAction Implementation Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md An Action that orchestrates user registration, including creating the user via a Command and sending a welcome email. It uses `boot()` to inject the `WelcomeEmailSender` dependency. ```php class RegisterUserAction { private WelcomeEmailSender $welcomeEmail; public function __construct( public readonly string $name, public readonly string $email, public readonly string $password, ) { app()->call([$this, 'boot']); } public function boot(WelcomeEmailSender $welcomeEmail): void { $this->welcomeEmail = $welcomeEmail; } public function execute(): User { $user = (new CreateUserCommand( name: $this->name, email: $this->email, password: $this->password, ))->execute(); $this->welcomeEmail->send($user); return $user; } } ``` -------------------------------- ### Register User Action Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt This Action orchestrates the creation of a new user and sends a welcome email. It resolves its MailerService dependency in boot(). ```php class RegisterUserAction { private MailerService $mailer; public function __construct( public readonly string $name, public readonly string $email, public readonly string $password, ) { app()->call([$this, 'boot']); } public function boot(MailerService $mailer): void { $this->mailer = $mailer; } public function execute(): User { $user = (new CreateUserCommand( name: $this->name, email: $this->email, password: $this->password, ))->execute(); $this->mailer->sendWelcome($user); return $user; } } ``` -------------------------------- ### Usage of Queries Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Demonstrates how to execute both a simple Query (`GetPublishedPostsQuery`) and a Query requiring dependency injection (`FindPostsByTagQuery`). ```php $posts = (new GetPublishedPostsQuery(limit: 10))->execute(); $tagged = (new FindPostsByTagQuery(tag: 'laravel'))->execute(); ``` -------------------------------- ### CreatePostCommand Implementation Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md A Command that handles the creation of a new post. It takes business parameters in the constructor and uses Eloquent to create the record. No `boot()` method is needed if no external dependencies are required. ```php class CreatePostCommand { public function __construct( public readonly int $authorId, public readonly string $title, public readonly string $body, ) {} public function execute(): Post { return Post::query()->create([ 'author_id' => $this->authorId, 'title' => $this->title, 'body' => $this->body, ]); } } ``` -------------------------------- ### Registering Instance Support Service Provider Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 03 - Toolkit and Optional Utilities.md This code snippet shows how the toolkit registers the InstanceSupportServiceProvider, which in turn checks for and registers the application's InstanceServiceProvider if it exists. ```php if (class_exists("Utilitarian\Instance\InstanceServiceProvider::class)) { $this->app->register( \Utilitarian\Instance\InstanceServiceProvider::class ); } ``` -------------------------------- ### Use Separate Routes for Wizard Steps Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Implement multi-step wizards using separate routes and actions for each distinct operation. Avoid consolidating multiple wizard steps into a single endpoint. ```php // Correct: separate routes for wizard steps Route::post('/onboarding/company', CreateCompanyAction::class); Route::post('/onboarding/team', InviteTeamMembersAction::class); Route::post('/onboarding/payment', SetupPaymentMethodAction::class); // Incorrect: single endpoint handling multiple steps Route::post('/onboarding', CompleteOnboardingAction::class); ``` -------------------------------- ### Controller Action Selection Logic Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 01 - HTTP Layer and Entry Points.md Demonstrates acceptable controller logic for choosing which action to dispatch based on request data. Avoids embedding business rules directly within the controller. ```php public function store(Request $request) { if ($request->boolean('publish')) { $action = CreateAndPublishPostAction::make(...); } else { $action = CreatePostAction::make(...); } return $action->dispatch(); } ``` ```php public function store(Request $request) { $discount = $request->user()->isPremium() ? 0.2 : 0; // Business rule $finalPrice = $this->calculatePrice($request->items, $discount); // Business logic // ... } ``` -------------------------------- ### Create and Publish Post Action Class Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Defines a multi-step Action that creates a post, publishes it, and notifies followers. It uses dependency injection for the notification client. ```php class CreateAndPublishPostAction { private NotificationClient $notifications; public function __construct( public readonly int $authorId, public readonly string $title, public readonly string $body, ) { app()->call([$this, 'boot']); } public function boot(NotificationClient $notifications): void { $this->notifications = $notifications; } public function execute(): Post { $author = (new GetAuthorQuery(id: $this->authorId))->execute(); $post = (new CreatePostCommand( authorId: $this->authorId, title: $this->title, body: $this->body, ))->execute(); (new PublishPostCommand(postId: $post->id))->execute(); $this->notifications->notifyFollowers($author, $post); return $post; } } ``` -------------------------------- ### FindPostsByTagQuery with boot() Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md A Query that finds posts by tag using an external `SearchIndexClient`. The `boot()` method is used to inject this dependency via the service container. ```php class FindPostsByTagQuery { private SearchIndexClient $search; public function __construct( public readonly string $tag, ) { app()->call([$this, 'boot']); } public function boot(SearchIndexClient $search): void { $this->search = $search; } public function execute(): Collection { return $this->search->findByTag($this->tag); } } ``` -------------------------------- ### Execute User Registration Action Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Instantiates and executes a RegisterUserAction with data from a request. ```php $user = (new RegisterUserAction( name: $request->string('name'), email: $request->string('email'), password: $request->string('password'), ))->execute(); ``` -------------------------------- ### Update Action Invocation in execute() (Before Toolkit) Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Shows direct instantiation and execution of a command within an Action's execute method before using the toolkit. ```php // Before: $user = (new CreateUserCommand( name: $this->name, email: $this->email, password: $this->password, ))->execute(); ``` -------------------------------- ### Update Action Invocation in execute() (After Toolkit) Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Demonstrates invoking a command using ::make()->dispatch() within an Action's execute method after integrating the toolkit. ```php // After: $user = CreateUserCommand::make( name: $this->name, email: $this->email, password: $this->password, )->dispatch(); ``` -------------------------------- ### Implicit Binding as Existence Guard Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Use implicit binding to verify a resource's existence (returning 404 if not found) when no complex business rules or access controls are involved. Domain resolution should be handled separately. ```php Route::get('/posts/{post}', function (Post $post) { return GetPostWithCommentsQuery::make($post->id)->dispatch(); }); ``` ```php Route::get('/tenants/{tenant}/active-subscription', function (Tenant $tenant) { // Business logic leaking into route layer return $tenant->subscriptions()->active()->first(); }); ``` -------------------------------- ### Find Posts By Tag Query (With Bootstrapped Dependency) Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt A query that depends on an external `SearchService`. The `boot()` method is used to resolve and inject this dependency. Use when your query needs to interact with services beyond Eloquent. ```php // Query with boot — external service dependency class FindPostsByTagQuery { private SearchService $search; public function __construct( public readonly string $tag, ) { app()->call([$this, 'boot']); } public function boot(SearchService $search): void { $this->search = $search; } public function execute(): Collection { return $this->search->findByTag($this->tag); } } // Usage $tagged = (new FindPostsByTagQuery(tag: 'laravel'))->execute(); ``` -------------------------------- ### Feature/Domain Grouping for Actions, Commands, Queries Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Chapter 03 - Structure and Naming.md Organize code by feature or domain, mirroring the structure across Actions, Commands, and Queries. Keep the directory structure shallow, with a maximum of two levels deep. ```php app/ ├── Actions/ │ ├── Auth/ │ │ ├── RegisterUserAction.php │ │ └── LoginUserAction.php │ ├── Order/ │ │ ├── CreateOrderAction.php │ │ └── ProcessPaymentAction.php │ └── Post/ │ ├── CreateAndPublishPostAction.php │ └── UpdatePostAction.php ├── Commands/ │ ├── Auth/ │ │ └── CreateUserCommand.php │ ├── Order/ │ │ ├── CreateOrderCommand.php │ │ └── UpdateOrderStatusCommand.php │ └── Post/ │ ├── CreatePostCommand.php │ └── PublishPostCommand.php └── Queries/ ├── Order/ │ ├── GetOrderQuery.php │ └── GetUserOrdersQuery.php └── Post/ ├── GetPostsQuery.php └── GetPublishedPostsQuery.php ``` -------------------------------- ### Route Closure for Creating Posts Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Handles an incoming POST request to create a post using the CreateAndPublishPostAction and returns the created post as JSON. ```php Route::post('/posts', function (Request $request) { $post = (new CreateAndPublishPostAction( authorId: $request->user()->id, title: $request->string('title'), body: $request->string('body'), ))->execute(); return response()->json($post, 201); })->name('posts.create'); ``` -------------------------------- ### Infrastructure Adapter Structure Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Chapter 03 - Structure and Naming.md When using infrastructure adapters, structure directories as {DataSource}/{DomainEntity}/{UseCase}. This keeps the infrastructure adapter explicit and the domain structure scalable. ```php app/ └─ Airtable/ ├─ Order/ │ ├─ FindOrderById.php │ ├─ ListOrders.php │ └─ OrderRecordMapper.php ├─ Customer/ └─ Product/ ``` -------------------------------- ### Action Naming Convention Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Chapter 03 - Structure and Naming.md Actions should follow the pattern {Verb(s)}{Noun}Action. Use 'And' to combine multiple operations for distinct steps. Actions can be more descriptive than commands. ```php RegisterUserAction ProcessOrderAction CreateAndPublishPostAction CancelAndRefundOrderAction ``` -------------------------------- ### Create User Command with Email Value Object Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Demonstrates using the Email Value Object within a Command constructor. This guarantees that the email passed to the command is always valid and normalized. ```php // Usage — validation happens once at construction, trusted everywhere after class CreateUserCommand { public function __construct( public readonly string $name, public readonly Email $email, // guaranteed valid & normalized public readonly string $password, ) { app()->call([$this, 'boot']); } // ... } ``` -------------------------------- ### Request-Scoped Context with Middleware and Controller Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 01 - HTTP Layer and Entry Points.md Illustrates how middleware can resolve and set request-scoped data using `$request->attributes`, and how controllers can retrieve and pass this data explicitly to actions. This ensures clear data flow and testability. ```php // Middleware — resolves and stores context public function handle(Request $request, Closure $next): Response { $scope = (new ResolveShopScopeQuery($request))->execute(); $request->attributes->set(ShopScope::class, $scope); return $next($request); } ``` ```php // Controller — reads attributes, passes explicitly to Action public function show(string $slug, Request $request): Response { $scope = $request->attributes->get(ShopScope::class); return (new ShowProductAction($slug, $scope))->execute(); } ``` ```php // Action — receives scope as a plain constructor parameter public function __construct( public readonly string $slug, public readonly ShopScope $scope, ) {} ``` -------------------------------- ### Post Controller Store Method Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md Handles the storage of a new post within a controller, utilizing a StorePostRequest and the CreateAndPublishPostAction. ```php class PostController { public function store(StorePostRequest $request): JsonResponse { $post = (new CreateAndPublishPostAction( authorId: $request->user()->id, title: $request->validated('title'), body: $request->validated('body'), ))->execute(); return new JsonResponse(new PostResource($post), 201); } } ``` -------------------------------- ### Define API Routes with Semantic Parameters Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Use semantic parameter names for routes. Prefixes are applied only when all nested routes share mandatory context. Route names should express business intent. ```php // Semantic parameter names (not {id}, {uuid}) Route::get('/orders/{order}', ShowOrderAction::class); Route::get('/payments/{payment}', ShowPaymentAction::class); // NOT /orders/{order}/payments/{payment} // Prefixes only when ALL nested routes share mandatory context Route::prefix('tenants/{tenant}')->group(function () { Route::get('/dashboard', TenantDashboardAction::class); Route::get('/settings', TenantSettingsAction::class); }); // Route names express business intent, not CRUD verbs Route::post('/orders', CreateOrderAction::class)->name('orders.create'); Route::post('/orders/{order}/ship', ShipOrderAction::class)->name('orders.ship'); // NOT orders.update // Distinct user intentions = distinct routes Route::post('/posts/{post}/publish', PublishPostAction::class); Route::post('/posts/{post}/archive', ArchivePostAction::class); Route::post('/posts/{post}/feature', FeaturePostAction::class); // NOT: Route::patch('/posts/{post}', UpdatePostAction::class) // Multi-step wizard: separate route per step Route::post('/onboarding/company', CreateCompanyAction::class); Route::post('/onboarding/team', InviteTeamMembersAction::class); Route::post('/onboarding/payment', SetupPaymentMethodAction::class); // Closure as micro-controller: single statement, no branching Route::get('/posts/{post}', fn (Post $post) => (new GetPostDetailsQuery(id: $post->id))->execute() ); ``` -------------------------------- ### Singular Directory Naming Convention Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Chapter 03 - Structure and Naming.md Use singular directory names to represent concepts or bounded contexts, such as Order/, User/, or Payment/. Avoid plural names like Orders/ unless the directory is inherently a collection (e.g., Reports/). ```php Order/ FindOrder.php ListOrders.php SyncOrders.php ``` ```php User/ Payment/ ``` -------------------------------- ### Separate Routes for Distinct User Intents Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Create distinct routes for operations that represent different user intentions, even if they modify the same underlying entity. This clarifies the domain interface. ```php // Correct: distinct user intentions Route::post('/posts/{post}/publish', PublishPostAction::class); Route::post('/posts/{post}/archive', ArchivePostAction::class); Route::post('/posts/{post}/feature', FeaturePostAction::class); // Incorrect: single update endpoint for multiple intents Route::patch('/posts/{post}', UpdatePostAction::class); ``` -------------------------------- ### HTTP Controller for Request Orchestration Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Use controllers as transport adapters to coordinate HTTP requests and choose appropriate actions. Avoid placing business logic directly within controllers. ```php class PostController { public function store(StorePostRequest $request): JsonResponse { // Acceptable: choosing which action based on request data $action = $request->boolean('publish') ? new CreateAndPublishPostAction( authorId: $request->user()->id, title: $request->validated('title'), body: $request->validated('body'), ) : new CreatePostAction( authorId: $request->user()->id, title: $request->validated('title'), body: $request->validated('body'), ); return new JsonResponse(new PostResource($action->execute()), 201); } } ``` -------------------------------- ### Map POST Endpoints to Single Business Operations Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Ensure each POST route corresponds to exactly one business operation. Avoid generic endpoints that handle multiple unrelated use cases internally. ```php // Correct: one route per operation Route::post('/invoices/{invoice}/send', SendInvoiceAction::class); Route::post('/invoices/{invoice}/void', VoidInvoiceAction::class); Route::post('/invoices/{invoice}/refund', RefundInvoiceAction::class); // Incorrect: single route handling multiple operations Route::post('/invoices/{invoice}/process', ProcessInvoiceAction::class); // (internally branches on 'action' parameter: send, void, refund) ``` -------------------------------- ### Use Route Closures for Simple Actions Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Prefer route closures when a route only needs to invoke a single action and return its result. This avoids the overhead of creating an unnecessary controller class. ```php // Preferred: no controller needed Route::get('/posts', fn () => GetPublishedPostsQuery::make()->dispatch()); // Unnecessary controller class PostController { public function index() { return GetPublishedPostsQuery::make()->dispatch(); } } ``` -------------------------------- ### Route Closure for Simple Endpoints Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt For simple, single-response routes, a closure is preferred over an empty controller. This keeps the codebase lean for straightforward use cases. ```php Route::get('/posts', fn () => (new GetPublishedPostsQuery())->execute()); ``` -------------------------------- ### Route Prefixes for Mandatory Context Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Employ route prefixes only when all nested routes share a common, mandatory context parameter. Do not group capability-based or independently addressable resources under contextual prefixes. ```php Route::prefix('tenants/{tenant}')->group(function () { Route::get('/dashboard', TenantDashboardAction::class); Route::get('/settings', TenantSettingsAction::class); }); ``` ```php Route::prefix('orders/{order}')->group(function () { Route::get('/payments/{payment}', ShowPaymentAction::class); }); ``` -------------------------------- ### Misuse of Logging Infrastructure for Context Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 01 - HTTP Layer and Entry Points.md Shows an unacceptable pattern where Laravel's Context facade is used to pass request-scoped data. This obscures data flow and is not intended for explicit dependency injection. ```php // Middleware Context::addHidden(ShopScope::class, $scope); // Controller $scope = Context::getHidden(ShopScope::class); ``` -------------------------------- ### Cross-Domain Action Placement Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Chapter 03 - Structure and Naming.md When an action affects multiple domains, place it within the directory of the primary affected aggregate. Avoid 'Shared' or 'Common' directories for cross-domain actions. ```php app/ └── Actions/ └── Order/ └── CancelOrderAndCreateRefundAction.php # Lives in Order, affects Payment ``` -------------------------------- ### Route by Owning Aggregate Identifier Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Use the resource identifier alone when it's globally unique and its parent can be derived. Avoid including parent identifiers in the URL if they are redundant. ```php Route::get('/payments/{payment}', ShowPaymentAction::class); ``` ```php Route::get('/orders/{order}/payments/{payment}', ShowPaymentAction::class); ``` -------------------------------- ### GetPublishedPostsQuery Implementation Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 04 - Light Version (No Toolkit).md A Query that retrieves published posts, limited by a specified count. This version does not require a `boot()` method as it directly uses Eloquent. ```php class GetPublishedPostsQuery { public function __construct( public readonly int $limit = 20, ) {} public function execute(): Collection { return Post::query() ->where('status', 'published') ->latest() ->limit($this->limit) ->get(); } } ``` -------------------------------- ### Misuse of Global Helpers for Context Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 01 - HTTP Layer and Entry Points.md Highlights the unacceptability of using global helper functions to access request-scoped data, as this creates hidden coupling and bypasses explicit data flow. ```php $id = currentShopId(); // hidden coupling, untestable $locale = activeLocale(); // bypasses the explicit data flow ``` -------------------------------- ### Direct Data Resolution in Controller Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 01 - HTTP Layer and Entry Points.md Resolve necessary data directly within the controller when only a few routes require it. This avoids the overhead of middleware for simple cases. ```php public function show(string $slug, Request $request, ResolveShopScopeQuery $query): Response { $scope = $query->execute($request); return (new ShowProductAction($slug, $scope))->execute(); } ``` -------------------------------- ### Passing Request-Scoped Context via Attributes Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Use middleware to resolve request-scoped context and set it on the request attributes. Controllers should then explicitly read and pass this context to actions. ```php // Middleware public function handle(Request $request, Closure $next): Response { $scope = (new ResolveShopScopeQuery($request))->execute(); $request->attributes->set(ShopScope::class, $scope); // class name as key avoids collisions return $next($request); } ``` ```php // Controller reads attribute and passes explicitly to Action public function show(string $slug, Request $request): Response { $scope = $request->attributes->get(ShopScope::class); return (new ShowProductAction($slug, $scope))->execute(); } ``` -------------------------------- ### Route Closures for Single Response Statements Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Route closures are acceptable for simple actions with a single response statement and no conditional logic. For branching or multiple statements, use a controller. ```php Route::get('/posts/{post}', fn (Post $post) => GetPostDetailsQuery::make($post->id)->dispatch() ); ``` ```php Route::get('/posts/{post}', function (Post $post) { if ($post->published) { return GetPublishedPostQuery::make($post->id)->dispatch(); } else { return GetDraftPostQuery::make($post->id)->dispatch(); } }); ``` -------------------------------- ### Name Routes with Business Meaning Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Assign route names that reflect the business intent or domain operation, rather than the technical action being performed. This improves readability and maintainability. ```php // Correct: business meaning Route::post('/orders', CreateOrderAction::class) ->name('orders.create'); Route::post('/orders/{order}/ship', ShipOrderAction::class) ->name('orders.ship'); // Incorrect: technical action Route::post('/orders', CreateOrderAction::class) ->name('orders.store'); Route::post('/orders/{order}/ship', ShipOrderAction::class) ->name('orders.update'); ``` -------------------------------- ### Semantic Route Parameter Naming Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Name route parameters after the domain concept they represent, irrespective of their internal resolution mechanism (e.g., ID, UUID, slug). ```php Route::get('/orders/{order}', ShowOrderAction::class); ``` ```php Route::get('/subscriptions/{subscription}', ShowSubscriptionAction::class); ``` ```php Route::get('/orders/{id}', ShowOrderAction::class); ``` ```php Route::get('/subscriptions/{uuid}', ShowSubscriptionAction::class); ``` -------------------------------- ### Implicit Binding in Trusted Contexts Source: https://github.com/utilitarian-dev/utilitarian-architecture/blob/main/Laravel/Laravel 02 - Routing Rules.md Implicit route model binding is suitable for simple, internal contexts like admin panels where no additional business rules apply. Avoid it for public or domain-critical flows. ```php Route::get('/admin/users/{user}', function (User $user) { return view('admin.users.show', ['user' => $user]); }); ``` ```php Route::get('/invoices/{invoice}/download', function (Invoice $invoice) { // Missing: authorization, capability check, audit logging return $invoice->downloadPdf(); }); ``` -------------------------------- ### Email Value Object Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt A Value Object for email addresses that enforces validation and normalization upon construction. It ensures that email values are always in a valid and consistent format. ```php class Email { public readonly string $value; public function __construct(string $raw) { $normalized = strtolower(trim($raw)); if (! filter_var($normalized, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException("Invalid email: {$raw}"); } $this->value = $normalized; } public function __toString(): string { return $this->value; } } ``` -------------------------------- ### Define Reusable Query Builder Modifiers Source: https://context7.com/utilitarian-dev/utilitarian-architecture/llms.txt Define modifiers as callable classes to encapsulate reusable SQL fragments. Use them with Laravel's `tap()` helper to compose complex queries. ```php class PublishedPostModifier { public function __invoke(Builder $query): Builder { return $query ->where('status', 'published') ->whereNotNull('published_at') ->where('published_at', '<=', now()); } } class ByAuthorModifier { public function __construct(private readonly int $authorId) {} public function __invoke(Builder $query): Builder { return $query->where('author_id', $this->authorId); } } ``` ```php class GetPublishedPostsByAuthorQuery { public function __construct( public readonly int $authorId, public readonly int $limit = 20, ) {} public function execute(): Collection { return Post::query() ->tap(new PublishedPostModifier()) ->tap(new ByAuthorModifier($this->authorId)) ->latest('published_at') ->limit($this->limit) ->get(); } } // Usage $posts = (new GetPublishedPostsByAuthorQuery(authorId: 42, limit: 5))->execute(); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.