Domain Driven Design: Components
Previously, we explored the fundamental concepts and terminology utilized in Domain Driven Design (DDD), as well as the layers that make up its architecture. In this section, we will examine the artifacts employed to integrate these concepts and construct our application, using PHP as an example.
Domain Driven Design
Domain Driven Design: Introduction
Domain Driven Design: Components
Entity
Entities are objects that are accessible with an identity in our application. In fact, an entity is a set of properties that have a unique identifier. A row of database table would be a good example. An entity is mutable, because it can change its attributes and also it has a lifecycle, meaning it can be deleted.
Consider Person as our entity, with id as identifier (for example autoincrement):
<?php
class Person
{
private int $id;
private string $name;
private string $surname;
public function __construct(int $id, string $name, string $surname)
{
$this->id = $id;
$this->name = $name;
$this->surname = $surname;
}
public function id(): int
{
return $this->id;
}
public function name(): string
{
return $this->name;
}
public function surname(): string
{
return $this->surname;
}
}
Value Object (VO)
Unlike the entity value object is an object that contains attributes but has no conceptual identity. They are immutable objects. Their values don't change and have no lifecycle (it means they are not just like a row of your database table that can be deleted).
Encapsulating integer ID number in a value object (with extra comparison method) will look like as following:
<?php
class Id
{
private int $value;
public function __construct(int $value)
{
$this->value = $value;
}
public function value(): int
{
return $this->value;
}
public function isBiggerThan(Id $otherId): bool
{
return $this->value() > $otherId->value();
}
}
Data Transfer Object (DTO)
Data transfer object is an object that carries data between processes. DTO does not have any behavior except for storage, retrieval, serialization and deserialization of its own data (mutators, accessors, parsers and serializers). In other words, DTOs are simple objects that should not contain any business logic but may contain serialization and deserialization mechanisms for transferring data over the wire.
To transfer some client data to another part of the system, we can use the following DTO:
<?php
class Client
{
private string $name;
private string $surname;
public function __construct(string $name, string $surname)
{
$this->name = $name;
$this->surname = $surname;
}
public static function fromPrimitives(array $primitives): self
{
return new self((string) $primitives['name'], (string) $primitives['surname']);
}
public function toPrimitives(): array
{
return [
'name' => $this->name,
'surname' => $this->surname,
];
}
public function name(): string
{
return $this->name;
}
public function surname(): string
{
return $this->surname;
}
}
Aggregate
They represent a collection of objects that are connected to each other, creating a set of relationships, with the goal to treat them as units. Moreover, they also have an aggregate root. Aggregate roots are objects that own other objects. This is the only entity that any object outside of the aggregate can reference to. For example, an Order Line object doesn't make sense without an Order to belong to, so we say that the Order is the aggregate root.
Let's see a basic aggregate for the example of Orders and Lines we mentioned above:
<?php
final class Order
{
private int $id;
private array $lines = [];
private function __construct(int $orderId)
{
$this->id = $orderId;
}
public static function create(int $orderId): Order
{
return new self($orderId);
}
public function addLine(int $productId, int $quantity): void
{
$lineNumber = count($this->lines) + 1;
$this->lines[] = new OrderLine($lineNumber, $productId, $quantity);
}
public function orderId(): int
{
return $this->id;
}
}
final class OrderLine
{
private int $lineNumber;
private int $productId;
private int $quantity;
public function __construct(int $lineNumber, int $productId, int $quantity)
{
$this->lineNumber = $lineNumber;
$this->productId = $productId;
$this->quantity = $quantity;
}
}
Domain Event
In many business models, you need to be able to describe things that happen and change the state of the model. For this, you can use domain events. A domain event is anything that happened in the domain model that may be of interest to other parts of the system.
The following code shows the minimum interface for basic domain events and the basic event:
<?php
interface DomainEvent
{
public function eventId(): int;
public function occurredOn(): DateTimeImmutable;
}
class UserRegistered implements DomainEvent
{
private int $eventId;
private int $userId;
public function __construct(int $eventId, int $userId)
{
$this->eventId = $eventId;
$this->userId = $userId;
$this->occurredOn = new DateTimeImmutable();
}
public function eventId(): int
{
return $this->eventId;
}
public function userId(): int
{
return $this->userId;
}
public function occurredOn(): DateTimeImmutable
{
return $this->occurredOn;
}
}
Note: as we mentioned, event is something that already happened so the name (UserRegistered) must be verb past tense.
Service
The term service refers to a software functionality or a set of software functionalities (such as the retrieval of specified information or the execution of a set of operations) with a purpose that different clients can reuse for different purposes, together with the policies that should control its usage.
Repository
A Repository is basically a layer that sits between your project’s domain and the database. This means that you should think of accessing the data in your database in the same way as you would work with a standard collection object.
The programmer must not be aware of the details needed to access a database. As the database is in the infrastructure layer, it has to deal with infrastructure details instead of dealing with domain concepts.
Finally, the repository acts as a storage place for globally accessible objects and may also include a strategy. It may access one persistence storage or another based on the specified strategy.
Factory
In the OOP world, a Factory refers to an object that has the single responsibility of creating other objects. In Domain-Driven Design, Factories are used to encapsulate the knowledge necessary for object creation.
A basic example on how factory pattern works (we suppose that classes and interface in use sections are already defined):
<?php
use Car;
use Bus;
use Truck;
use VehicleInterface;
class VehicleFactory
{
public static function build(string $type): VehicleInterface
{
if ($type === 'car') {
return new Car();
}
if ($type === 'bus') {
return new Bus();
}
if ($type === 'truck') {
return new Truck();
}
throw new Exception('Type is not defined');
}
}
0 Comments