Domain::handle() returned array<string, mixed>. The Responder passed that
array to view(), which hands it to extract(), so its keys were the
template's variable names — renaming $tagline in a view meant editing a
business-logic class. That is the coupling the Responder sits in the
pipeline to absorb, and while it lasted the Responder did nothing but
forward its argument.
framework/Interfaces/DomainResult.php is an empty marker. It declares
nothing because it has nothing to say about what a result holds; its job
is to give Domain::handle() and Action::respond() a type that is neither
array nor object, so the pipeline can still be read.
The framework cannot enforce the rest — Actions\, Domains\ and
Responders\ are application namespaces and the framework must not depend
on them. So it ships the interface, the stubs, and the tests that hold
the stubs to the contract:
- Result.txt is new: a final readonly value object under Domains\Results\
- Domain.txt returns one; Responder.txt takes it and names the view's
variables itself; View.txt renders one of them, so a generated feature
demonstrates the whole flow rather than static text
- make:feature writes the Result before the Domain, because the Domain's
return type names it, and creates app/Domains/Results/
- StubsTest asserts the Domain stub no longer returns array, that the
Result implements DomainResult, and that the Responder stub does not
forward its argument to view() unchanged
No stub gained a placeholder, so the placeholder contract is unchanged.
A Payload carrying a status enum was considered and left out. One result
type per outcome does the same work with no framework machinery: a
feature that can miss returns a different class when it misses, and the
Responder picks the view and the status code from the type it was handed.
Verified by generating a feature into a linked skeleton checkout and
requesting it — the generated Action, Result, Domain, Responder and view
render a 200 carrying the result's value.
BREAKING: Domain::handle() returns a DomainResult, not an array;
Action::respond() takes one; a Responder's __invoke() takes its feature's
result type rather than array $data. An application must add a result
class per feature and move view-variable naming into its Responders.