Domain Driven Design: Components

  • avatar
  • 928 Views
  • 2 Likes
  • 5 mins read

We already discussed about the basic concepts and terms in Domain Driven Design (DDD), check out the Introduction post if you missed it. Also we explained which Layers compose the DDD architecture. Now let's see the artifacts we use to unify these concepts and build our application (with PHP example).

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 occurredOn(): DateTimeImmutable;
}

class UserRegistered implements DomainEvent
{
private int $userId;

public function __construct(int $userId)
{
$this->userId = $userId;
$this->occurredOn = new DateTimeImmutable();
}

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');
}
}

 Join Our Newsletter

Get the latest news and articles to your inbox every month

0 Comments

Leave a Reply

Your email address will not be published.