Domain Model in an Expressive app


#1

Hello.

I’m looking at using Expressive - but coming from a ZF1/ZF2 monolithic application background.

In the Expresive documentation there is no mention of models or the domain model (quite a thing in ZF1/2).

Search for ‘model’ at https://zend-expressive.readthedocs.io/en/latest/ or https://docs.zendframework.com/zend-expressive/ returns nothing about the domain model.

I presume this fits into the innermost middleware layer - yet I see no mention of this in the docs or getting started guides. Perhaps this is assumed to be obvious - or am I missing something?

Kevin


#2

There is no model defined by ZF2, ZF3 or expressive. Model is a complex topic, it is the core of the application and there is no one size fits all. It is up to the user to implement it however he see fit.

That being said, controller and middlewares work with the model in a same way: they map requests to model actions and then model state to the view:

AddAlbum middleware:

public function process(ServerRequestInterface $request, DelegateInterface $delegate)
{
    $postData = $request->getParsedBody();
    $this->albumForm->setData($postData);
    if ($this->albumForm->isValid()) {
        $album = new AlbumEntity();
        $album->exchangeArray($form->getData());
        $id = $this->albumRepository->saveAlbum($album);
        return new RedirectResponse(
            $this->router->generateUri('album', ['id' => $id])
        );
    }
    return $delegate->process($request);
}

Or album controller:

public function addAction()
{
    $request = $this->getRequest();
    if (! $request->isPost()) {
        return ['form' => $form];
    }
    $this->albumForm->setData($request->getPost());
    if (! $this->albumForm->isValid()) {
        return ['form' => $form];
    }

    $album = new AlbumEntity();
    $album->exchangeArray($form->getData());
    $id = $this->albumRepository->saveAlbum($album)
    return $this->redirect()->toRoute('album', ['id' => $id]);
}

Model itself is not some class or object, it is an application layer that consists of many parts.
It can have just entities and mappers or repositories in its simplest form - that is what you see in various Album tutorials.
While complex models can consist of many layers themselves. There are entities, read models, commands, events, message buses, handlers, repositories, application and domain services and much much more.
It is really way outside the framework scope.


#3

While @xerkus replies is certainly valid and interesting, I feel it might not be practical enough for this question.

While not the only choice available, Doctrime ORM is a common solution to implement models nowadays. And it can be used with Zend Expressive. I would recommend dasprid/container-interop-doctrine for the integration.

And then you could write your models with something similar to:

<?php

declare(strict_types=1);

namespace App\Model;

use Doctrine\ORM\Mapping as ORM;

/**
 * User
 * 
 * @ORM\MappedSuperclass
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 */
class User
{
    /**
     * @var string
     *
     * @ORM\Column(type="string", length=50, unique=true)
     */
    private $login = '';

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=255)
     */
    private $password = '';

    /**
     * Set login (eg: johndoe)
     *
     * @param string $login
     */
    public function setLogin(string $login): void
    {
        $this->login = $login;
    }

    /**
     * Get login (eg: johndoe)
     *
     * @return string
     */
    public function getLogin(): string
    {
        return $this->login;
    }

    /**
     * Encrypt and change the user password
     *
     * @param string $password
     */
    public function setPassword(string $password): void
    {
        $this->password = password_hash($password, PASSWORD_DEFAULT);
    }

    /**
     * Returns the hashed password
     *
     * @return string
     */
    public function getPassword(): string
    {
        return $this->password;
    }
}

Usage of those models and repositories in your middlewares would be very similar to what @xerkus explained.

For more details, and a fully working example, I suggest to have a look at the following two successive commits:

  1. First DB connection
  2. First doctrine entity and migrations

Finally, if you happen to want to build a GraphQL API based on those models, then follow the next commit, and you’ll find how to integrate ecodev/graphql-doctrine to automatically expose all your getters and setters through GraphQL.