tl;dr: Instead of inspecting a Domain result to determine how to present it, consider using a Domain Payload Object to wrap the results of Domain interaction and simulataneously indicate the status of the attempted interaction. The Domain already knows what the results mean; let it provide that information explicitly instead of attempting to re-discover it at presentation time.
In Action-Domain-Responder the Action passes input to the Domain layer, which then returns some data for the Action to pass to the Responder. In simple scenarios, it might be enough for the Responder to inspect the data to determine how it should present that data. In more complex scenarios, though, it would make more sense for the Domain to pass back the data in a way that indicates the status of the data. Instead of the Responder inspecting the Domain results, the Domain should tell us what kind of results they are.
For example, let’s look at an example of some older code to update a Blog post. This is MVC-ish code, not ADR code; we’ll refactor along the way.
<?php
class BlogController
{
// POST /blog/{id}
public function update($id)
{
$blog = $this->model->fetch($id);
if (! $blog) {
// 404 Not Found
// (no blog entry with that ID)
$this->response->status->set(404);
$this->view->setData(array('id' => $id));
$content = $this->view->render('not-found');
$this->response->body->setContent($content);
return;
}
$data = $this->request->post->get('blog');
if (! $blog->update($data)) {
// update failure, but why?
if (! $blog->isValid()) {
// 422 Unprocessable Entity
// (not valid)
$this->response->status->set(422);
$this->view->setData(array('blog' => $blog));
$content = $this->view->render('update');
$this->response->body->setContent($content);
return;
} else {
// 500 Server Error
// (i.e., valid data, but update failed for some other reason)
$this->response->status->set(500);
return;
}
}
// 200 OK
// (i.e., the update worked)
$this->response->status->set(200);
$this->view->setData(array('blog' => $blog));
$content = $this->view->render('update');
$this->response->body->setContent($content);
}
}
?>
We can see that there is some amount of model work going on here (look for a blog post, attempt to update it if it exists, check for error conditions on the update attempt). There is also some amount of presentation work going on; remember, the view is not the template – the view is the response. So, even though the view templates are separated, the HTTP status codes are also part of the presentation, meaning that there is an insuffcient level of separation of concerns.
In converting this to Action-Domain-Responder, we can pretty easily extract the model work to a Domain, and the presentation work to a Responder, resulting in something like the following. (Note that the Domain layer now adds values to the returned $blog
entity to indicate different failure states.)
<?php
class BlogUpdateAction
{
// POST /blog/{id}
public function __invoke($id)
{
$data = $this->request->post->get('blog');
$blog = $this->domain->update($id, $data);
$this->responder->setData('blog' => $blog);
$this->responder->__invoke();
}
}
class BlogUpdateResponder
{
public function __invoke()
{
if (! $blog) {
// 404 Not Found
// (no blog entry with that ID)
$this->response->setStatus(404);
$this->view->setData(array('id' => $id));
$content = $this->view->render('not-found');
$this->response->body->setContent($content);
return;
}
if ($blog->updateFailed()) {
// 500 Server Error
// (i.e., valid data, but update failed for some other reason)
$this->response->status->set(500);
return;
}
if (! $blog->isValid()) {
// 422 Unprocessable Entity
// (invalid data submitted)
$this->response->setStatus(422);
$this->view->setData(array('blog' => $blog));
$content = $this->view->render('update');
$this
Truncated by Planet PHP, read more at the original (another 3442 bytes)
more
{ 0 comments... » Action-Domain-Responder and the “Domain Payload” Pattern read them below or add one }
Post a Comment