ma2mori
Posted on June 5, 2024
Domain Driven Design (DDD) Practice: Live Streaming App Example
Introduction
In previous article, we learned the basic concepts of Domain Driven Design (DDD). In this article, we will introduce a concrete practical example using a “live-streaming application” as a subject.
Overview of a live-streaming application
A live-streaming app is a platform where users distribute content in real-time and other users watch it. It has the following features
- Viewing users can also be delivery users
- Viewers can provide feedback to the distributor in the form of free and paid reactions
Therefore, the main modeling considerations are
- User management
- Distribution Management
- Reaction management
In practice, we will work with domain experts to work out the details.
Domain Modeling
First, we will define the main entities, value objects, aggregates, services, and repositories for the live distribution application.
Entity
-
User
- Attributes: user id, name, profile information, follower list.
-
distribution
- Attributes: distribution ID, distributor (user), start time, end time, viewer list
value object
-
Profile Information
- Attributes: age, gender, self, etc.
-
Comment
- Attributes: comment content, poster (user), time posted
-
Reaction
- Attributes: reaction type (free, paid), reaction content, reaction time
aggregation
-
Distribution Aggregate
- Aggregation of related entities (comments, reactions) around a delivery
Services
-
Distribution Management Service
- Start and end distribution, manage viewers, aggregate reactions.
repository
-
User Repository
- Retrieve and save user information.
-
Distribution Repository
- Retrieve and store distribution information
Use Cases
In this section, we will take a practical look at the elements of DDD through a simple use case of a live-streaming application. The following example is in PHP.
Use Case: Start and End of Delivery
-
Start distribution
- When a user starts a delivery, a new delivery entity is created.
<?php
class ProfileInfo {
public function __construct(
private int $age, private string $gender, private string $age, private string $gender
private string $gender, private string $bio
private string $bio
) {}
// getter methods
}
class User {
private array $followers; }
public function __construct(
private string $userId, private string $name, private string $followers
private string $name, private
private ProfileInfo $profileInfo, private
array $followers = [] )
) {
$this->followers = array_map(fn($follower) => new UserId($follower), $followers); }
}
// getter methods
}
class UserId {
public function __construct(private string $id) {}
public function __toString(): string {
return $this->id; }
}
}
class LiveStream {
private DateTime $startTime;
private ?DateTime $endTime; }
private array $viewers; private
public function __construct(
private string $streamId, private
private User $streamer
) {
$this->startTime = new DateTime();
$this->endTime = null;
$this->viewers = []; }
}
public function endStream(): void {
$this->endTime = new DateTime(); }
}
public function addViewer(User $viewer): void {
$this->viewers[] = $viewer; }
}
// other methods...
}
-
End of delivery
- When the delivery is finished, the end time of the delivery entity is set and the required data is saved.
<?php
class LiveStreamRepository {
private array $liveStreams = [];
public function save(LiveStream $liveStream): void {
$this->liveStreams[$liveStream->streamId] = $liveStream;
}
public function findById(string $streamId): ?LiveStream {
return $this->liveStreams[$streamId] ? null;?
}
}
class LiveStreamService {
public function __construct(private LiveStreamRepository $repository) {}
public function startStream(string $streamId, User $streamer): void {
$liveStream = new LiveStream($streamId, $streamer);
$this->repository->save($liveStream); }
}
public function endStream(string $streamId): void {
$liveStream = $this->repository->findById($streamId);
if ($liveStream) {
$liveStream->endStream();
$this->repository->save($liveStream); }
}
}
}
Use Case: Sending Reactions
-
Send Free Reactions
- This is a use case where a user sends a free reaction.
<?php
class Reaction {
private DateTime $time;.
public function __construct(
private string $type, private string $content
private string $type, private string $content
) {
$this->time = new DateTime(); }
}
// getter methods
}
class LiveStream {
private array $reactions = [];
public function addReaction(Reaction $reaction): void {
$this->reactions[] = $reaction; }
}
// other methods...
}
-
Sending a Paid Reaction
- Use case where a user sends a paid reaction.
<?php
class PaidReaction extends Reaction {
public function __construct(
string $content,.
private float $amount
) {
parent::__construct('paid', $content);
}
// getter methods
}
domain-model-diagram
Benefits of the DDD Approach
The benefits of this approach are as follows.
-
Clear separation of business logic.
- Centralizing the business logic in the domain model improves code readability and maintainability.
-
High level of abstraction.
- Abstraction of complex domains into concepts such as entities, value objects, services, and repositories simplifies and strengthens the design.
-
Ensures consistency.
- Ensures consistency throughout the system by guaranteeing data integrity within the boundaries of the aggregation.
-
Ease of Testing.
- Clearly defined domain model facilitates unit and integration testing.
-
Extensibility.
- Models can be easily extended and modified to meet changing business requirements.
Conclusion
This article introduced the basic concepts and practices of Domain Driven Design (DDD) using a live-streaming application as an example; using the DDD approach, you will be able to effectively manage complex business logic. I look forward to continuing to learn more in order to create more valuable software.
Posted on June 5, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.