Libdomain is a php library to facilitate domain driven development.
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 ofEntity
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.
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')
]);
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
]);
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')]);
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.