cjsaylor/libdomain

This package is abandoned and no longer maintained. No replacement package was suggested.

PHP classes and traits to facilitate domain driven design.

4.0.0 2018-07-23 11:21 UTC

This package is auto-updated.

Last update: 2020-02-12 13:41:32 UTC


README

Libdomain is a php library to facilitate domain driven development.

Build Status

Core concepts

This library contains the following abstract classes:

  • Entity - An entity is an identifiable object.
  • ValueObject - An object who's values are immutable.
  • Collection - A collection of Entity objects.
  • CollectionEntity - An entity object that also acts as a collection (an identifiable collection).

In cases where the abstract classes can't be extended, all functionality of the abstract classes are provided with traits. For example, if you wanted an existing entity to have the properties of a ValueObject, then you would use the ReadAccessable trait:

use Cjsaylor\Domain\ValueObject\ValueObjectInterface;
use Cjsaylor\Domain\Behavior\ReadAccessable;

class ConcreteEntity implements ValueObjectInterface {
  use ReadAccessable;
}

The ConcreteEntity would now be an immutable value object.

Examples

Entity Example

The first example illustrates a user object that accepts an email attribute that must be an immutable email value object.


use \Cjsaylor\Domain\Entity;
use \Cjsaylor\Domain\ValueObject;

class Email extends ValueObject
{
  public function __construct(string $value) {
    // Validate an email address here
    $this['value'] = $value;
  }

  public function __toString() {
    return $this['value'];
  }
}

class User extends Entity
{
  public function offsetSet($offset, $value) : void
  {
    if ($offset === 'email' && !$value instanceof Email) {
      throw new \LogicException('Email must be an email value object!');
    }
    parent::offsetSet($offset, $value);
  }
}

$user = new User([
  'email' => new Email('user@somedomain.com')
]);

CollectionEntity example

This next example will illustrate a group of users where that group also has an identity. Here we will make use of the CollectionEntity which is both a Collection and an Entity. It will make use of the User entity defined in the first example.

use \Cjsaylor\Domain\CollectionEntity;

class UserGroup extends CollectionEntity
{
  // Here, we set the expectation that this collection can take only users
  public function __construct(array $data = [], User ...$users) {
    parent::__construct($data, ...$users);
  }

  // Here's a method to add additional users post-construction
  public function add(User $user) {
    $this->getItems()[] = $user;
  }
}

$users = [
  new User([
    'id' => 1,
    'email' => new Email('user@somedomain.com')
  ]),
  new User([
    'id' => 2,
    'email' => new Email('user2@somedomain.com')
  ])
];

$userGroup = new UserGroup([
  'id' => 1,
  ...$users
]);

Setter callback example

Let's modify the User object with some custom setter callbacks (available in 1.0.1). This allows us to typehint (and do other custom set logic for the Email value object).

class User extends Entity {

  public function setEmail(Email $email) {
    $this->data['email'] = $email;
  }

}

// Would produce an error as `setEmail` would be called and would not match the type.
$user = new User(['email' => 'user@somedomain.com']);

// Valid
$user = new User(['email' => new Email('user@somedomain.com')]);

Concrete entities example

In some instances, we don't want extra properties to be set on our entities. To limit the properties that can be set on an entity, the PropertyLimitable interface/trait can be implemented:

use Cjsaylor\Domain\Behavior\PropertyLimitable;
use Cjsaylor\Domain\Behavior\PropertyLimitTrait;

class User extends Entity implements PropertyLimitable {
  use PropertyLimitTrait;

  public function concreteAttributes() {
    return ['id', 'email'];
  }

}

$user = new User();
$user['id'] = 1; // OK!
$user['first_name'] = 'Chris'; // Has no affect and is not set.