Business models

Edit on GitHub

Business models are classes where you program the business logic of your system. Business logic includes all kinds of algorithms (for example, cart calculation), storage procedures (for example, checkout save workflow), and interactions with external providers (for example, payment).

Business models are not visible from other layers and bundles. Although their methods are public, they must not be used directly from the outside. In Java, the package-private access modifier is used so that methods can only be called from within the same package or module. In PHP, this modifier is missing, so instead of an explicit declaration, there’s only a convention.

Dependency injection

Business models must not directly depend on each other. The dependency container injects dependencies to the constructor, and we use interfaces not to depend on concrete classes. This is very important for testability and decoupling. It also lets you use country-specific extensions of models.

A typical constructor and the related properties of a business model are as follows:

<?php
...
class KeyManager implements KeyManagerInterface
{
    /**
     * @var GlossaryQueryContainerInterface
     */
    protected $queryContainer;

    /**
     * @var KeySourceInterface
     */
    protected $keySource;

    /**
     * @param KeySourceInterface $keySource
     * @param GlossaryQueryContainerInterface $queryContainer
     */
    public function __construct(KeySourceInterface $keySource, GlossaryQueryContainerInterface $queryContainer)
    {
        $this->keySource = $keySource;
        $this->queryContainer = $queryContainer;
    }
...

As you can see, the KeyManager class depends on two other classes, which are specified by their related interfaces. The constructor gets them injected and provides them as properties for the model’s methods.

Info

The KeyManager class implements KeyManagerInterface, which is used by other models that depend on KeyManager.

Every business model is shipped with an interface that exposes all public methods.

Avoid hybrids

If you need data objects, you can use transfer objects and entities or your own DTOs for internal use. In any case, do not mix them up with business models. Each class is either a stateless model with functional methods or a data object with getters and setters.

Avoid hybrids is a rule from the Clean Code book.

The following example is a violation of this rule because it mixes:

<?php
...
public function __construct(ModelInterface $myModel, array $data)
{
    $this->myModel = $myModel;
    $this->data = $data;
}
...

To generate related code, you might use the following definitions:

  • vendor/bin/console spryk:run AddZedBusinessModel: Add Zed Business model.
  • vendor/bin/console spryk:run AddZedBusinessModelInterface: Add Zed business model interface.
  • vendor/bin/console spryk:run AddZedBusinessModelInterfaceMethod: Add Zed business model interface method.
  • vendor/bin/console spryk:run AddZedBusinessModelMethod: Add Zed business model method.

For details, see Spryks.