0% found this document useful (0 votes)
263 views

Architecture of Complex Web Applications

This document discusses the architecture of complex web applications using Laravel PHP. It covers topics like dependency injection, refactoring, the application layer, error handling, validation, events, unit testing and implementing a domain layer. The document provides many code examples and explanations of concepts and design patterns to help build maintainable applications.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
263 views

Architecture of Complex Web Applications

This document discusses the architecture of complex web applications using Laravel PHP. It covers topics like dependency injection, refactoring, the application layer, error handling, validation, events, unit testing and implementing a domain layer. The document provides many code examples and explanations of concepts and design patterns to help build maintainable applications.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 257

Architecture of complex web

applications
With examples in Laravel(PHP)

Adel F
This book is for sale at
http://leanpub.com/architecture-of-complex-web-applications

This version was published on 2019-03-28

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean
Publishing process. Lean Publishing is the act of publishing an in-progress ebook
using lightweight tools and many iterations to get reader feedback, pivot until you
have the right book and build traction once you do.

© 2018 - 2019 Adel F


Contents

1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

2. Bad Habits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
SRP violation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
CRUD-style thinking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
The worship of PHP dark magic . . . . . . . . . . . . . . . . . . . . . . . . . . 10
“Rapid” Application Development . . . . . . . . . . . . . . . . . . . . . . . . 12
Saving lines of code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Other sources of pain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3. Dependency injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Single responsibility principle . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Image uploader example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Extending interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Static methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

4. Painless refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
“Static” typing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Model fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

5. Application layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Request data passing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Work with database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
CONTENTS

Service classes or command classes . . . . . . . . . . . . . . . . . . . . . . . . 86

6. Error handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Base exception class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Global handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Checked and unchecked exceptions . . . . . . . . . . . . . . . . . . . . . . . . 101
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

7. Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Database related validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Two levels of validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Validation by annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
Value objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
VO as composition of values . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Value object is not for user data validation . . . . . . . . . . . . . . . . . . . 124
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

8. Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Database transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Queues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Using Eloquent events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Entities as event fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

9. Unit testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141


First steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Testing stateful classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Testing classes with dependencies . . . . . . . . . . . . . . . . . . . . . . . . . 149
Software testing types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
Laravel testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Application layer unit testing . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Application testing strategy . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173

10. Domain layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175


When and why? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
Implementation of Domain layer . . . . . . . . . . . . . . . . . . . . . . . . . 182
CONTENTS

Error handling in domain layer . . . . . . . . . . . . . . . . . . . . . . . . . . 212


Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214

11. CQRS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216


Reading and writing - different responsibilities? . . . . . . . . . . . . . . . . 216
Typical Application layer class . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Command Query Responsibility Segregation . . . . . . . . . . . . . . . . . . 222
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

12. Event sourcing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227


The kings game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
Write model unit tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
World without magic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
Implementing ES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Unique data in ES systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247

13. Sagas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248


Multi-system “transactions” . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
Orchestration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250

14. Useful books and links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252


Classic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
DDD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
ES and CQRS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
Unit testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
1. Introduction
“Software Engineering Is Art Of Compromise” wiki.c2.com

I have seen many projects evolve from a simple “MVC” structure. Many developers
explain the MVC pattern like this: “View is a blade file, Model is an Eloquent entity,
and one Controller to rule them all!” Well, not one, but every additional logic
usually implemented in a controller. Controllers become very large containers of
very different logic (image uploading, 3rd party APIs calling, working with Eloquent
models, etc.). Some logic are moved to base controllers just to reduce the copy-
pasting. The same problems exist both in average projects and in big ones with 10+
millions unique visitors per day.
The MVC pattern was introduced in the 1970-s for the Graphical User Interface.
Simple CRUD web applications are just an interface between user and database,
so the reinvented “MVC for web” pattern became very popular. Although, web
applications can easily become more than just an interface for a database. What does
the MVC pattern tell us about working with binary files(images, music, videos…),
3rd party APIs, cache? What are we to do if entities have non-CRUD behavior? The
answer is simple: Model is not just an Active Record (Eloquent) entity; it contains all
logic that work with an application’s data. More than 90 percent of a usual modern
complex web application code is Model in terms of MVC. Symfony framework
creator Fabien Potencier once said: “I don’t like MVC because that’s not how the
web works. Symfony2 is an HTTP framework; it is a Request/Response framework.”
The same I can say about Laravel.
Frameworks like Laravel contain a lot of Rapid Application Development features,
which help to build applications very fast by allowing developers to cut corners. They
are very useful in the “interface for database” phase, but later might become a source
of pain. I did a lot of refactorings just to remove them from the projects. All these
auto-magical things and “convenient” validations, like “quickly check that this email
Introduction 2

is unique in this table” are great, but the developer should fully understand how they
work and when better to implement it another way.
On the other hand, advice from cool developers like “your code should be 100% cov-
ered by unit tests”, “don’t use static methods”, and “depend only upon abstractions”
can become cargo cults for some projects. I saw an interface IUser with 50+ properties
and class User: IUser with all these properties copy-pasted(it was a C# project). I
saw a huge amount of “abstractions” just to reach a needed percentage of unit test
coverage. These pieces of advice are good, but only for some cases and they must be
interpreted correctly. Each best practice contains an explanation of what problem it
solves and which conditions the destination project should apply. Developers have
to understand them before using them in their projects.
All projects are different. Some of them are suitable for some best practices. Others
will be okay without them. Someone very clever said: “Software development is
always a trade-off between short term and long term productivity”. If I need some
functionality already implemented in the project in another place, I can just copy-
paste it. It will be very productive in the short term, but very quickly it will start to
create problems. Almost every decision about refactoring or using a pattern has the
same dilemma. Sometimes, it is a good decision to not use some pattern that makes
the code “better”, because the positive effect for the project will not be worth the
amount of time needed to implement it. Balancing between patterns, techniques and
technologies and choosing the most suitable combination of them for the current
project is one the most important skills of a software developer/architect.
In this book, I talk about common problems appearing in projects and how developers
usually solve them. The reasons and conditions for using these solutions are a very
important part of the book. I don’t want to create a new cargo cult :)
I have to warn you:

• This book is not for beginners. To understand the problems I will talk about,
the developer should at least have participated in one project.
• This book is not a tutorial. Most of the patterns will be observed superficially,
just to inform the reader about them and how and when they can help. The
links to useful books and articles will be at the end of the book.
• The example code will never be ideal. I can call some code “correct” and still
find a lot of problems in it, as shown in the next chapter.
2. Bad Habits
SRP violation
In this chapter, I’ll try to show how projects, written according to documentation,
usually grow up. How developers of real projects try to fight with complexity. Let’s
start with a simple example.

public function store(Request $request)


{
$this->validate($request, [
'email' => 'required|email',
]);

$user = User::create($request->all());

if(!$user) {
return redirect()->back()->withMessage('...');
}

return redirect()->route('users');
}

public function update($id, Request $request)


{
//almost the same
}

It was very easy to write. Then, new requirements appear. Add avatar uploading in
create and update forms. Also, an email should be sent after registration.
Bad Habits 4

public function store(Request $request)


{
$this->validate($request, [
'email' => 'required|email',
'avatar' => 'required|image',
]);

$avatarFileName = ...;
\Storage::disk('s3')->put(
$avatarFileName, $request->file('avatar'));

$user = new User($request->except('avatar'));


$user->avatarUrl = $avatarFileName;
$user->save();

\Email::send($user, 'Hi email');

return redirect()->route('users');
}

Some logic should be copied to update method. But, for example, email sending
should happen only after user creation. The code still looks okay, but the amount of
copy-pasted code grows. Customer asks for a new requirement - automatically check
images for adult content. Some developers just add this code(usually, it’s just a call
for some API) to store method and copy-paste it to update. Experienced developers
extract upload logic to new controller method. More experienced Laravel developers
find out that file uploading code becomes too big and instead create a class, for ex-
ample, ImageUploader, where all this upload and adult content-checking logic will
be. Also, a Laravel facade(Service Locator pattern implementation) ImageUploader
will be introduced for easier access to it.
Bad Habits 5

/**
* @returns bool|string
*/
public function upload(UploadedFile $file)

This function returns false if something wrong happens, like adult content or S3 error.
Otherwise, uploaded image url.

public function store(Request $request)


{
...
$avatarFileName = \ImageUploader::upload(
$request->file('avatar'));

if($avatarFileName === false) {


return %some_error%;
}
...
}

Controller methods became simpler. All image uploading logic was moved to another
class. Good. The project needs image uploading in another place and we already have
a class for it! Only one new parameter will be added to upload method: a folder where
images should be stored will be different for avatars and publication images.

public function upload(UploadedFile $file, string $folder)

New requirement - immediately ban user who uploaded adult content. Well, it
sounds weird, because current image analysis tools aren’t very accurate, but it’s a
requirement(it was a real requirement in one of my projects!).
Bad Habits 6

public function upload(UploadedFile $file, string $folder)


{
...
if(check failed) {
$this->banUser(\Auth::user());
}
...
}

New requirement - application should not ban user if he uploads something wrong
to private places.

public function upload(


UploadedFile $file,
string $folder,
bool $dontBan = false)

When I say “new requirement”, it doesn’t mean that it appears the next day. In big
projects it can take months and years and can be implemented by another developer
who doesn’t know why the code is written like this. His job - just implement this
task as fast as possible. Even if he doesn’t like some code, it’s hard to estimate how
much time this refactoring will take in a big system. And, much more important, it’s
hard to not break something. It’s a very common problem. I hope this book will help
to organize your code to make it more suitable for safe refactoring. New requirement
- user’s private places needs weaker rules for adult content.

public function upload(


UploadedFile $file,
string $folder,
bool $dontBan = false,
bool $weakerRules = false)

The last requirement for this example - application shouldn’t ban user immediately.
Only after some tries.
Bad Habits 7

public function upload(


UploadedFile $file,
string $folder,
bool $dontBan = false,
bool $weakerRules = false,
int $banThreshold = 5)
{
//...
if(check failed && !$dontBan) {
if(\RateLimiter::tooManyAttempts(..., $banThreshold)) {
$this->banUser(\Auth::user());
}
}
//...
}

Okay, this code doesn’t look good anymore. Image upload function has a lot of
strange parameters about image checking and user banning. If user banning process
should be changed, developers have to go to ImageUploader class and implement
changes there. upload function call looks weird:

\ImageUploader::upload(
$request->file('avatar'), 'avatars', true, false);

Single Responsibility Principle was violated here. ImageUploader class has also
some other problems but we will talk about them later. As I mentioned before, store
and update methods are almost the same. Let’s imagine some very big entity with
huge logic and image uploading, other API’s calling, etc.
Bad Habits 8

public function store(Request $request)


{
// tons of code
// especially if some common code haven't
// extracted to classes like ImageUploader
}

public function update($id, Request $request)


{
// almost the same tons of code
}

Sometimes a developer tries to remove all this copy-paste by extracting the method
like this:

protected function updateOrCreateSomething(..., boolean $update)


{
if($update)...
if($update)...
if(!$update)...
}

I saw this kind of method with 700+ lines. After many requirement changes, there
were a huge amount of if($update) checks. This is definitely the wrong way to
remove copy-pasting. When I refactored this method by creating different create
and update methods and extracting similar logic to their own methods/classes, the
code become much easier to read.

CRUD-style thinking
The REST is very popular. Laravel developers use resource controllers with ready
store, update, delete, etc. methods even for web routes, not only for API. It looks
very simple. Only 4 verbs: GET(read), POST(create), PUT/PATCH(update) and
DELETE(delete). It is simple when your project is just a CRUD application with
Bad Habits 9

create/update forms and lists with a delete button. But when the application becomes
a bit more complex, the REST way becomes too hard. For example, I googled “REST
API ban user” and the first three results with some API’s documentation were very
different.

PUT /api/users/banstatus
params:
UserID
IsBanned
Description

POST /api/users/ban userId reason

POST /api/users/un-ban userId

PUT /api/users/{id}/status
params:
status: guest, regular, banned, quarantine

There also was a big table: which status can be changed to which
and what will happen

As you see, any non-standard verb(ban) and REST becomes not so simple. Especially
for beginners. Usually, all other methods are implemented by the update method.
When I asked in one of the seminars how to implement user banning with REST, the
first answer was:

PUT /api/users/{id}
params:
IsBanned=true

Ok. IsBanned is the property of User, but when user actually was banned, we
should send, for example, an email for this user. This requirement consequences
very complicated conditions with comparing “old” and “new” values on user update
operation. Another example: password change.
Bad Habits 10

PUT /api/users/{id}
params:
oldPassword=***
password=***

oldPassword is not a user property. So, another condition at user update. This
CRUD-style thinking, as I call it, affects even the user interface. I always remember
“typical Apple product, typical Google product” image as the best illustration of the
problem.

The worship of PHP dark magic


Sometimes developers just don’t(or don’t want to) see a simple way to implement
something. They write code with reflection, magic methods or other PHP dynamic
features, code which was hard to write and will be very hard to read! I used to do it
regularly. Like everyone, I think. I’ll show a little funny example.
I had a class for cache keys in one of my projects. We need keys in at least 2 places:
reading/creating cache value and clearing it before it expires(in cases when entity
was changed). Obvious solution:

final class CacheKeys


{
public static function getUserByIdKey(int $id)
{
return sprintf('user_%d_%d', $id, User::VERSION);
}

public static function getUserByEmailKey(string $email)


{
return sprintf('user_email_%s_%d',
$email,
User::VERSION);
}
//...
Bad Habits 11

$key = CacheKeys::getUserByIdKey($id);

Do you remember the dogma “Don’t use static functions”? Almost always, it’s
true. But this is a good example of exception. We will talk about it in further in
the Dependency Injection chapter. Well, when another project needed the same
functionality, I showed this class to the developer and said you can do the same.
After some time, he said that this class “isn’t very beautiful” and committed this
code:

/**
* @method static string getUserByIdKey(int $id)
* @method static string getUserByEmailKey(string $email)
*/
class CacheKeys
{
const USER_BY_ID = 'user_%d';
const USER_BY_EMAIL = 'user_email_%s';

public static function __callStatic(


string $name, array $arguments)
{
$cacheString = static::getCacheKeyString($name);
return call_user_func_array('sprintf',
array_prepend($arguments, $cacheString));
}

protected static function getCacheKeyString(string $input)


{
return constant('static::' . static::getConstName($input));
}

protected static function getConstName(string $input)


{
return strtoupper(
Bad Habits 12

static::fromCamelCase(
substr($input, 3, strlen($input) - 6))
);
}

protected static function fromCamelCase(string $input)


{
preg_match_all('<huge regexp>', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
$match = $match == strtoupper($match)
? strtolower($match)
: lcfirst($match);
}
return implode('_', $ret);
}
}

$key = CacheKeys::getUserById($id);

Shortly, this code transforms “getUserById” string to “USER_BY_ID” and uses this
constant value. A lot of developers, especially young ones, like to make this kind of
“beautiful” code. Sometimes, it can save a lot of lines of code. Sometimes not. But it’s
always hard to debug and support. The developer should think 10 times before using
any “cool” dynamic feature of language.

“Rapid” Application Development


Some framework creators also like dynamic features. In small projects they really
help to write quickly. But by using these cool features we are losing control of our app
execution and when the project grows, it starts to create problems. In the previous
example with cache keys, *::VERSION constants were forgotten, because it wasn’t
very simple to use it with this “optimization”. Another example; Laravel apps have a
lot of the same code like this:
Bad Habits 13

class UserController
{
public function update($id)
{
$user = User::find($id);
if($user === null)
{
abort(404);
}
//...
}
}

Laravel starting from some version suggests to use implicit route binding. This code
does the same as previous:

Route::post('api/users/{user}', 'UserController@update');

class UserController
{
public function update(User $user)
{
//...
}
}

It definitely looks better and reduces a lot of “copy-pasted” code. Later, the project
can grow and caching will be implemented. For GET queries, it is better to use cache,
but not for POST (there are a lot of reasons to not use cached entities in update
operations). Another possible issue: different databases for read and write queries.
It happens when one database server can’t serve all of a project’s queries. Usually,
database scaling starts from creating one database to write queries and one or more
read databases. Laravel has convenient configurations for read&write databases. So,
the route binding code can now look like this:
Bad Habits 14

Route::bind('user', function ($id) {


// get and return cached version or abort(404);
});

Route::bind('userToWrite', function ($id) {


return App\User::onWriteConnection()->find($id) ?? abort(404);
});

Route::get('api/users/{user}', 'UserController@edit');
Route::post('api/users/{userToWrite}', 'UserController@update');

It looks so strange and so easy to make a mistake. It happened because instead of


explicitly getting entities by id, the developer used implicit “optimization”. The first
example can be shortened like this:

class UserController
{
public function update($id)
{
$user = User::findOrFail($id);
//...
}
}

There is no need to “optimize” this one line of code. Frameworks suggest a lot of
other ways to lose control of your code. Be very careful with them.
A few words about Laravel’s convenient configuration for read&write databases.
It’s really convenient, but again, we lose control here. It isn’t smart enough. It just
uses read connection to select queries and write connection to insert/update/delete
queries. Sometimes we need to select from a write connection. It can be solved with
::onWriteConnection() helpers. But, for example, lazy loading relation will be fetched
from read connection again! In some very rare cases it made our data inconsistent.
Can you imagine how difficult it was to find this bug? In Laravel 5.5, one option
was added to fix that. It will send each query to write database after the first write
database query. This option partially solves the problem, but looks so weird.
Bad Habits 15

As a conclusion, I can say this: “Less magic in the code - much easier to debug and
support it”. Very rarely, in some cases, like ORM, is it okay to make some magic, but
only there.

Saving lines of code


When I was studying software engineering in the university, sometimes one of
us showed examples of his super-short code. Usually it was one line of code
implementing some algorithm. Some days after authoring this code, one could spend
a minute or more to understand this one line, but still it was “cool”. My conditions of
cool code were changed since those days. Cool code for me is the code with minimum
time needed to read and understand it by any other developer. Short code is not
always the most readable code. Usually, several simple classes is much better than
one short but complicated class.
The funny real example from one of my projects:

public function userBlockage(


UserBlockageRequest $request, $userId)
{
/** @var User $user */
$user = User::findOrFail($userId);

$done = $user->getIsBlocked()
? $user->unblock()
: $user->block($request->get('reason'));

return response()->json([
'status' => $done,
'blocked' => $user->getIsBlocked()
]);
}

The developer wanted to save some lines of code and implemented user blocking
and unblocking in the same method. The problems started from naming. The not
Bad Habits 16

very precise ‘blockage’ noun instead of the natural ‘block’ and ‘unblock’ verbs. The
main problem is concurrency: two moderators could open the same user’s page and
try to block him. First one will block, but the other one will unblock! Some kind of
optimistic locking could solve this issue, but the idea of optimistic locking not very
popular in Laravel projects (I’ve found some packages, but they have less than 50
stars in github). The best solution is to create two separate methods for blocking and
unblocking.

Other sources of pain


I forgot to tell you about the main enemy - Copy-Paste Driven Development. I hope
the answer for “Why copy-pasted code is hard to support” is obvious. There are lots
of other ways to make applications unsupportable and non-extendable. This book is
about how to avoid these problems, and the good habits which make the code more
flexible and supportable.
Let’s begin!
3. Dependency injection
Single responsibility principle
You may have heard about the Single Responsibility Principle(SRP). It’s canonical
definition: every module, class, or function should have responsibility over a single
part of the functionality provided by the software. Many developers simplify the
definition to “program object should do only one thing”. This definition is not
very precise. Robert C. Martin changes the term “responsibility” to “reason to
change”: “A class should have only one reason to change”. “Reason to change” is
a more convenient concept and we can talk about architecture using it. Almost all
architectures and best practices are trying to help the code to be more prepared for
changes. However, applications are different; they have different requirements and
different kinds of changes.
I often read this: “If you place all your code in controllers, it violates SRP!”. Imagine an
application - simple CRUD(Create, Read, Update and Delete) where users only look
and change your data using these 4 operations. All applications are just interfaces for
creating, viewing, editing and removing data, and all operations go directly to the
database without any other processing.

public function store(Request $request)


{
$this->validate($request, [
'email' => 'required|email',
'name' => 'required',
]);

$user = User::create($request->all());

if(!$user) {
return redirect()->back()->withMessage('...');
Dependency injection 18

return redirect()->route('users');
}

What kind of changes can this app have? Add/remove fields, add/remove entities…
It’s hard to imagine something more. I don’t think this code violates SRP. Almost.
Theoretically, redirecting to another route change is possible, but it’s not very
important. I don’t see any reason to refactor this code. New requirements can appear
with application growth: user avatar image uploading and email sending:

public function store(Request $request)


{
$this->validate($request, [
'email' => 'required|email',
'name' => 'required',
'avatar' => 'required|image',
]);

$avatarFileName = ...;
\Storage::disk('s3')->put(
$avatarFileName, $request->file('avatar'));

$user = new User($request->except('avatar'));


$user->avatarUrl = $avatarFileName;

if(!$user->save()) {
return redirect()->back()->withMessage('...');
}

\Email::send($user->email, 'Hi email');

return redirect()->route('users');
}

Here are several responsibilities already. Something might be changed in image up-
loading or email sending. Sometimes it’s hard to catch the moment when refactoring
Dependency injection 19

should be started. If these changes appeared only for user entity, there is probably
no sense in changing anything. However, other parts of the application will for sure
use the image uploading feature.
I want to talk about two important characteristics of application code - cohesion and
coupling. They are very basic and SRP is just a consequence of them. Cohesion is the
degree to which all methods of one class or parts of another unit of code (function,
module) are concentrated in its main goal.
Close to SRP. Coupling between two classes (functions, modules) is the degree of
how much they know about each other. High coupling means that some knowledge is
shared between several parts of code and each change can cause a cascade of changes
in other parts of the application.

Current case with store method is a good illustration of losing code quality. It
contains several responsibilities - it loses cohesion. Image uploading responsibility
is implemented in a few different parts of the application - high coupling. It’s time
to extract this responsibility to its own class.
First try:
Dependency injection 20

final class ImageUploader


{
public function uploadAvatar(User $user, UploadedFile $file)
{
$avatarFileName = ...;
\Storage::disk('s3')->put($avatarFileName, $file);

$user->avatarUrl = $avatarFileName;
}
}

I gave this example because I constantly encounter the fact that developers, trying to
take out the infrastructure functionality, take too much with them. In this case, the
ImageUploader class, in addition to its primary responsibility (file upload), assigns
the value to the User class property. What is bad about this? The ImageUploader
class “knows” about the User class and its avatarUrl property. Any such knowledge
tends to change. You will also have to change the ImageUploader class. This is high
coupling again.
Lets try to write ImageUploader with a single responsibility:

final class ImageUploader


{
public function upload(string $fileName, UploadedFile $file)
{
\Storage::disk('s3')->put($fileName, $file);
}
}

Yes, this doesn’t look like a case where refactoring helped a lot. But let’s imagine that
ImageUploader also generates a thumbnail or something like that. Even if it doesn’t,
we extracted its responsibility to its own class and spent very little time on it. All
future changes with the image uploading process will be much easier.
Dependency injection 21

Dependency Injection
Well, we created ImageUploader class, but how do we use it in UserController::store
method?

$imageUploader = new ImageUploader();


$imageUploader->upload(...);

Or just make the upload method static and call it like this:

ImageUploader::upload(...);

It was easy, right? But now store method has a hard-coded dependency to the
ImageUploader class. Lets imagine a lot of methods with this hard dependency and
then the company decided to use another image storage. Not for all images, only for
some of them. How would developers usually implement that? They just create An-
otherImageUploader class and change ImageUploader to AnotherImageUploader
in all needed methods. But what happened? According SRP, each of these methods
should have only one reason to change. Why does changing the image storage cause
several changes in the ImageUploader class dependents?
Dependency injection 22

As you see, the application looks like metal grid. It’s very hard to take, for example,
the ImageUploader class and move it to another project. Or just unit test it. Image-
Uploader can’t work without Storage and ThumbCreator classes, and they can’t
work without their dependencies, etc. Instead of direct dependencies to classes, the
Dependency Injection technique suggests just to ask dependencies to be provided
to the class.

final class ImageUploader


{
/** @var Storage */
private $storage;

/** @var ThumbCreator */


private $thumbCreator;

public function __construct(


Storage $storage, ThumbCreator $thumbCreator)
{
$this->storage = $storage;
$this->thumbCreator = $thumbCreator;
Dependency injection 23

public function upload(...)


{
$this->thumbCreator->...
$this->storage->...
}
}

Laravel and many other frameworks contain “DI container” - a special service, which
takes all responsibilities of creating class instances and injecting them to other classes.
So, method store can be rewritten like this:

public function store(


Request $request, ImageUploader $imageUploader)
{
//...
$avatarFileName = ...;
$imageUploader->upload(
$avatarFileName, $request->file('avatar'));

//...
}

Here, the Laravel feature was used to request dependencies directly in the parameters
of the controller method. Dependencies have become softer. Classes do not create
dependency instances and do not require static methods. However, both the store
method and the ImageUploader class refer to specific classes. The Dependency In-
version principle says “High-level modules should not depend on low-level modules.
Both should depend on abstractions”. Abstractions should not depend on details.
Details should depend on abstractions. The requirement of abstraction in OOP
languages is interpreted unequivocally: dependence should be on interfaces, and
not on classes. However, I have repeatedly stated that projects are different. Let’s
consider two options.
You’ve probably heard about Test-driven Development (TDD) techniques. Roughly
speaking, TDD postulates writing tests at the same time as the code. A review of TDD
Dependency injection 24

techniques is beyond the scope of this book, so we will look at just one of its faces.
Imagine that you need to implement the ImageUploader class, but the Storage and
ThumbCreator classes are not yet available. We will discuss unit testing in detail in
the corresponding chapter, so we will not dwell on the test code now. You can simply
create the Storage and ThumbCreator interfaces, which are not yet implemented.
Then you can simply write the ImageUploader class and tests for it, creating mocks
from these interfaces (we will talk about mocks later).

interface Storage
{
//...Some methods
}

interface ThumbCreator
{
//...Some methods
}

final class ImageUploader


{
/** @var Storage */
private $storage;

/** @var ThumbCreator */


private $thumbCreator;

public function __construct(


Storage $storage, ThumbCreator $thumbCreator)
{
$this->storage = $storage;
$this->thumbCreator = $thumbCreator;
}

public function upload(...)


{
$this->thumbCreator->...
Dependency injection 25

$this->storage->...
}
}

class ImageUploaderTest extends TestCase


{
public function testSomething()
{
$storageMock = \Mockery::mock(Storage::class);
$thumbCreatorMock = \Mockery::mock(ThumbCreator::class);

$imageUploader = new ImageUploader(


$storageMock, $thumbCreatorMock);
$imageUploader->upload(...
}
}

The ImageUploader class still cannot be used in the application, but it has already
been written and tested. Later, when the implementations of these interfaces are
ready, you can configure the container in Laravel, for example:

$this->app->bind(Storage::class, S3Storage::class);
$this->app->bind(ThumbCreator::class, ImagickThumbCreator::class);

After that, the ImageUploader class can be used in the application. When the
container creates an instance of the ImageUploader class, it will create instances
of the required classes and substitute them instead of interfaces into the constructor.
TDD has proven itself in many projects where it is part of the standard. I also like
this approach. Developing with TDD, you get little comparable pleasure. However,
I have rarely seen its use. It imposes quite serious requirements on the developer for
architectural thinking. Developers need to know what to put in separate interfaces
and classes and decompose the application in advance.
Usually everything in projects is much simpler. First, the ImageUploader class is
written, in which the logic of creating thumbnails and the logic of saving everything
to the repository are concentrated. Then, perhaps, is the extraction of logic into
Dependency injection 26

the classes Storage and ThumbCreator, leaving only a certain orchestration over
these two classes in ImageUploader. Interfaces are not used. Occasionally a very
interesting event takes place in such projects - one of the developers reads about the
Dependency Inversion principle and decides that there are serious problems with
the architecture on the project. Classes do not depend on abstractions! Interfaces
should be introduced immediately! But the names ImageUploader, Storage, and
ThumbCreator are already taken. As a rule, in this situation, developers choose one
of two terrible ways to extract the interface.
The first is the creation of *Contracts namespace and the creation of all interfaces
there. As an example, Laravel source:

namespace Illuminate\Contracts\Cache;

interface Repository
{
//...
}

namespace Illuminate\Contracts\Config;

interface Repository
{
//...
}

namespace Illuminate\Cache;

use Illuminate\Contracts\Cache\Repository as CacheContract;

class Repository implements CacheContract


{
//...
}
Dependency injection 27

namespace Illuminate\Config;

use ArrayAccess;
use Illuminate\Contracts\Config\Repository as ConfigContract;

class Repository implements ArrayAccess, ConfigContract


{
//...
}

There is a double sin here: the use of the same name for the interface and class, as well
as the use of the same name for different program objects. The namespace feature
provides an opportunity for such detour maneuvers. As you can see, even in the
source code of classes, you have to use CacheContract and ConfigContract aliases.
For the rest of the project, we have 4 program objects with the name Repository. And
the classes that use the configuration and cache via DI look something like this (if
you do not use aliases):

use Illuminate\Contracts\Cache\Repository;

class SomeClassWhoWantsConfigAndCache
{
/** @var Repository */
private $cache;

/** @var \Illuminate\Contracts\Config\Repository */


private $config;

public function __construct(Repository $cache,


\Illuminate\Contracts\Config\Repository $config)
{
$this->cache = $cache;
$this->config = $config;
}
}
Dependency injection 28

Only variable names help to guess what dependencies are used here. However, the
names for Laravel-facades for these interfaces are quite natural: Config and Cache.
With such names for interfaces, the classes that use them would look much better.
The second option is to use the Interface suffix, as such: creating an interface with the
name StorageInterface. Thus, having class Storage implements StorageInterface,
we postulate that there is an interface and its default implementation. All other
classes that implement it, if they exist at all, appear secondary compared to Storage.
The existence of the StorageInterface interface looks very artificial: it was created
either to make the code conform to some principles, or only for unit testing. Such
a phenomenon is found in many languages. In C#, the IList interface and the List
class, for example. In Java, prefixes or suffixes to interfaces are not accepted, but this
often happens there:

class StorageImpl implements Storage

This is also the situation with the default implementation of the interface. There are
two possible situations:

1. There is an interface and several possible implementations. In this case, the


interface should be called natural. Implementations should have prefixes that
define them. Interface Storage. Class S3Storage implements Storage, class
FileStorage implements Storage.
2. There is an interface and one implementation. Another implementation looks
impossible. Then the interface is not needed. It is necessary to use a class with
a natural name.

If there is no direct need to use interfaces on the project, then it is quite normal to
use classes and Dependency Injection. Let’s look again at ImageUploader:
Dependency injection 29

final class ImageUploader


{
/** @var Storage */
private $storage;

/** @var ThumbCreator */


private $thumbCreator;

public function __construct(Storage $storage,


ThumbCreator $thumbCreator)
{
$this->storage = $storage;
$this->thumbCreator = $thumbCreator;
}

public function upload(...)


{
$this->thumbCreator->...
$this->storage->...
}
}

It uses some software objects Storage and ThumbCreator. The only thing he uses
is public methods. It absolutely doesn’t care whether it’s interfaces or real classes.
Dependency Injection, removing the need to instantiate objects from classes, gives
us super-abstraction: there is no need for classes to even know what type of program
object it is dependent on. At any time, when conditions change, classes can be
converted to interfaces with the allocation of functionality to a new class (S3Storage).
Together with the configuration of the DI-container, these will be the only changes
that will have to be made on the project. Of course, if it’s a public package, the
code must be written as flexibly as possible and all dependencies must be easily
replaceable, therefore interfaces are required. However, on a regular project, using
dependencies on real classes is an absolutely normal trade-off.
Dependency injection 30

Inheritance
Inheritance is called one of the main concepts of OOP and developers adore it.
However, quite often inheritance is used in the wrong key, when a new class needs
some kind of functionality and this class is inherited from the class that has this
functionality. A simple example. Laravel has an interface Queue and many classes
implementing it. Let’s say our project uses RedisQueue.

interface Queue
{
public function push($job, $data = '', $queue = null);
}

class RedisQueue implements Queue


{
public function push($job, $data = '', $queue = null)
{
// implementation
}
}

Once it became necessary to log all the tasks in the queue, the result was the
OurRedisQueue class, which was inherited from RedisQueue.

class OurRedisQueue extends RedisQueue


{
public function push($job, $data = '', $queue = null)
{
// logging

return parent::push($job, $data, $queue);


}
}

The task is completed: all push methods calls are logged. After some time, the
framework is updated and a new method pushOn appears in the Queue interface.
Dependency injection 31

It is actually a push alias, but with a different order of parameters. The expected
implementation appears in the RedisQueue class.

interface Queue
{
public function push($job, $data = '', $queue = null);
public function pushOn($queue, $job, $data = '');
}

class RedisQueue implements Queue


{
public function push($job, $data = '', $queue = null)
{
// implementation
}

public function pushOn($queue, $job, $data = '')


{
return $this->push($job, $data, $queue);
}
}

Because OurRedisQueue inherits the RedisQueue, we did not need to take any
action during the upgrade. Everything works as before and the team gladly began
using the new pushOn method.
In the new update, the Laravel team could, for some reason, do some refactoring.
Dependency injection 32

class RedisQueue implements Queue


{
public function push($job, $data = '', $queue = null)
{
return $this->innerPush(...);
}

public function pushOn($queue, $job, $data = '')


{
return $this->innerPush(...);
}

public function innerPush(...)


{
// implementation
}
}

Refactoring is absolutely natural and doesn’t change the class contract. It still
implements the Queue interface. However, after some time after this update, the
team notices that logging does not always work. It is easy to guess that now it will
only log push calls, not pushOn. When we inherit a non-abstract class, this class has
two responsibilities at a high level. A responsibility to their own clients, as well as to
the inheritors, who also use its functionality. The authors of the class may not even
suspect the second responsibility, and this can lead to complex, elusive bugs on the
project. Even such a simple example quite easily led to a bug that would not be so
easy to catch. To avoid such difficulties in my projects, all non-abstract classes are
marked as final, thus prohibiting inheritance from myself. The template for creating
a new class in my IDE contains the ‘final class’ instead of just the ‘class’. Final classes
have responsibility only to their clients.
By the way, Kotlin language designers seem to think the same way and decided to
make classes there final by default. If you want your class to be open for inheritance,
the ‘open’ or ‘abstract’ keyword should be used:
Dependency injection 33

open class Foo {}

I like this :)
However, the danger of inheriting an implementation is still possible. An abstract
class with protected methods and its descendants can fall into exactly the same sit-
uation that I described above. The protected keyword creates an implicit connection
between parent class and child class. Changes in the parent can lead to bugs in the
children. The DI mechanism gives us a simple and natural opportunity to ask for
the implementation we need. The logging issue is easily solved using the Decorator
pattern:

final class LoggingQueue implements Queue


{
/** @var Queue */
private $baseQueue;

/** @var Logger */


private $logger;

public function __construct(Queue $baseQueue, Logger $logger)


{
$this->baseQueue = $baseQueue;
$this->logger = $logger;
}

public function push($job, $data = '', $queue = null)


{
$this->logger->log(...);

return $this->>baseQueue->push($job, $data, $queue);


}
}

// configuring container in service provider


$this->app->bind(Queue::class, LoggingQueue::class);
Dependency injection 34

$this->app->when(LoggingQueue::class)
->needs(Queue::class)
->give(RedisQueue::class);

Warning: this code will not work in a real Laravel environment, because the
framework has a more complex procedure for initiating these classes. This container
configuration will inject an instance of LoggingQueue to anyone who wants to get a
Queue. LoggingQueue will get a RedisQueue instance as a constructor parameter.
The Laravel update with a new pushOn method results in an error - LoggingQueue
does not implement the required method. Thus, we immediately implement logging
of this method, also.
Plus, you probably noticed that we now completely control the constructor. In the
variant with inheritance, we would have to call parent::__construct and pass on
everything that it asks for. This would be an additional, completely unnecessary
link between the two classes. As you can see, the decorator class does not have any
implicit links between classes and allows you to avoid a whole class of troubles in
the future.

Image uploader example


Let’s return to the image uploader example from the previous chapter. Image-
Uploader class was extracted from controller to implement the image uploading
responsibility. Requirements for this class:

• uploaded image content should be checked for unappropriated content


• if the check is passed, image should be uploaded to some folder
• if the check is failed, user who uploaded this image should be banned after some
tries
Dependency injection 35

final class ImageUploader


{
/** @var GoogleVisionClient */
private $googleVision;

/** @var FileSystemManager */


private $fileSystemManager;

public function __construct(


GoogleVisionClient $googleVision,
FileSystemManager $fileSystemManager)
{
$this->googleVision = $googleVision;
$this->fileSystemManager = $fileSystemManager;
}

/**
* @param UploadedFile $file
* @param string $folder
* @param bool $dontBan
* @param bool $weakerRules
* @param int $banThreshold
* @return bool|string
*/
public function upload(
UploadedFile $file,
string $folder,
bool $dontBan = false,
bool $weakerRules = false,
int $banThreshold = 5)
{
$fileContent = $file->getContents();

// Some checking using $this->googleVision,


// $weakerRules and $fileContent
Dependency injection 36

if(check failed)
if(!$dontBan) {
if(\RateLimiter::..., $banThreshold)) {
$this->banUser(\Auth::user());
}
}

return false;
}

$fileName = $folder . 'some_unique_file_name.jpg';

$this->fileSystemManager
->disk('...')
->put($fileName, $fileContent);

return $fileName;
}

private function banUser(User $user)


{
$user->banned = true;
$user->save();
}
}

Basic refactoring
Simple image uploading responsibility becomes too big and contains some other
responsibilities. It definitely needs some refactoring.
If ImageUploader will be called from console command, the Auth::user() command
will return null and ImageUploader has to add a ‘!== null’ check to its code. Better to
provide the User object by another parameter(User $uploadedBy), which is always
not null. The user banning functionality can be used somewhere else. Now it’s only
Dependency injection 37

2 lines of code, but in the future it may contain some email sending or other actions.
Better to create a class for that.

final class BanUserCommand


{
public function banUser(User $user)
{
$user->banned = true;
$user->save();
}
}

Next, the “ban user after some wrong upload tries” responsibility. $banThreshold
parameter was added to the function parameters by mistake. It’s constant.

final class WrongImageUploadsListener


{
const BAN_THRESHOLD = 5;

/** @var BanUserCommand */


private $banUserCommand;

/** @var RateLimiter */


private $rateLimiter;

public function __construct(


BanUserCommand $banUserCommand,
RateLimiter $rateLimiter)
{
$this->banUserCommand = $banUserCommand;
$this->rateLimiter = $rateLimiter;
}

public function handle(User $user)


{
$rateLimiterResult = $this->rateLimiter
Dependency injection 38

->tooManyAttempts(
'user_wrong_image_uploads_' . $user->id,
self::BAN_THRESHOLD);

if($rateLimiterResult) {
$this->banUserCommand->banUser($user);
return false;
}
}
}

Our system’s wrong image uploading reaction might be changed in the future. These
changes will only affect this class. Next, the responsibility to remove is “image
content checking”:

final class ImageGuard


{
/** @var GoogleVisionClient */
private $googleVision;

public function __construct(


GoogleVisionClient $googleVision)
{
$this->googleVision = $googleVision;
}

/**
* @param string $imageContent
* @param bool $weakerRules
* @return bool true if content is correct
*/
public function check(
string $imageContent,
bool $weakerRules): bool
{
// Some checking using $this->googleVision,
Dependency injection 39

// $weakerRules and $fileContent


}
}

final class ImageUploader


{
/** @var ImageGuard */
private $imageGuard;

/** @var FileSystemManager */


private $fileSystemManager;

/** @var WrongImageUploadsListener */


private $listener;

public function __construct(


ImageGuard $imageGuard,
FileSystemManager $fileSystemManager,
WrongImageUploadsListener $listener)
{
$this->imageGuard = $imageGuard;
$this->fileSystemManager = $fileSystemManager;
$this->listener = $listener;
}

/**
* @param UploadedFile $file
* @param User $uploadedBy
* @param string $folder
* @param bool $dontBan
* @param bool $weakerRules
* @return bool|string
*/
public function upload(
UploadedFile $file,
Dependency injection 40

User $uploadedBy,
string $folder,
bool $dontBan = false,
bool $weakerRules = false)
{
$fileContent = $file->getContents();

if(!$this->imageGuard->check($fileContent, $weakerRules)) {
if(!$dontBan) {
$this->listener->handle($uploadedBy);
}

return false;
}

$fileName = $folder . 'some_unique_file_name.jpg';

$this->fileSystemManager
->disk('...')
->put($fileName, $fileContent);

return $fileName;
}
}

ImageUploader lost some responsibilities and is happy about it. It doesn’t care about
how to check images and what will happen with a user who uploaded something
wrong now. It only makes some orchestration job. But I still don’t like a parameters
of upload method. Responsibilities were removed from ImageUploader, but their
parameters are still there and upload method calls still look ugly:

$imageUploader->upload($file, $user, 'gallery', false, true);

Boolean parameters always look ugly and increase the cognitive load for reading the
code. The new boolean parameter might be added if the requirement to not check
images will appear… I’ll try to remove them two different ways:
Dependency injection 41

• OOP way
• Configuration way

OOP way
I’m going to use polymorphism, so I have to introduce interfaces.

interface ImageChecker
{
public function check(string $imageContent): bool;
}

final class StrictImageChecker implements ImageChecker


{
/** @var ImageGuard */
private $imageGuard;

public function __construct(


ImageGuard $imageGuard)
{
$this->imageGuard = $imageGuard;
}

public function check(string $imageContent): bool


{
return $this->imageGuard
->check($imageContent, false);
}
}

final class WeakImageChecker implements ImageChecker


{
/** @var ImageGuard */
private $imageGuard;
Dependency injection 42

public function __construct(


ImageGuard $imageGuard)
{
$this->imageGuard = $imageGuard;
}

public function check(string $imageContent): bool


{
return $this->imageGuard
->check($imageContent, true);
}
}

final class SuperTolerantImageChecker implements ImageChecker


{
public function check(string $imageContent): bool
{
return true;
}
}

ImageChecker interface and three implementations:

• StrictImageChecker for checking image content with strict rules


• WeakImageChecker for checking image content with weak rules
• SuperTolerantImageChecker for cases when image checking is not needed

WrongImageUploadsListener class becomes an interface with 2 implementations:


Dependency injection 43

interface WrongImageUploadsListener
{
public function handle(User $user);
}

final class BanningWrongImageUploadsListener


implements WrongImageUploadsListener
{
// implementation is the same.
// with RateLimiter and BanUserCommand
}

final class EmptyWrongImageUploadsListener


implements WrongImageUploadsListener
{
public function handle(User $user)
{
// Just do nothing
}
}

EmptyWrongImageUploadsListener class will be used instead of $dontBan pa-


rameter.

final class ImageUploader


{
/** @var ImageChecker */
private $imageChecker;

/** @var FileSystemManager */


private $fileSystemManager;

/** @var WrongImageUploadsListener */


private $listener;

public function __construct(


Dependency injection 44

ImageChecker $imageChecker,
FileSystemManager $fileSystemManager,
WrongImageUploadsListener $listener)
{
$this->imageChecker = $imageChecker;
$this->fileSystemManager = $fileSystemManager;
$this->listener = $listener;
}

/**
* @param UploadedFile $file
* @param User $uploadedBy
* @param string $folder
* @return bool|string
*/
public function upload(
UploadedFile $file,
User $uploadedBy,
string $folder)
{
$fileContent = $file->getContents();

if (!$this->imageChecker->check($fileContent)) {
$this->listener->handle($uploadedBy);

return false;
}

$fileName = $folder . 'some_unique_file_name.jpg';

$this->fileSystemManager
->disk('...')
->put($fileName, $fileContent);

return $fileName;
}
Dependency injection 45

The logic of boolean parameters was moved to interfaces and their implementors.
Working with FileSystemManager also can be simplified by creating a facade for
it (I’m talking about Facade pattern, not Laravel facades). The only problem now
is instantiating the configured ImageUploader instance for each client. It can be
solved by a combination of Builder and Factory patterns. This will give full control
of configuring the needed ImageUploader object to client code.
Also, it might be solved by configuring DI-container rules, which ImageUploader
object will be provided for each client. All configuration will be placed in one
container config file. I think for this task the OOP way looks too over-engineered. It
might be solved simply by one configuration file.

Configuration way
I’ll use a Laravel configuration file to store all needed configuration. config/im-
age.php:

return [
'disk' => 's3',

'avatars' => [
'check' => true,
'ban' => true,
'folder' => 'avatars',
],

'gallery' => [
'check' => true,
'weak' => true,
'ban' => false,
'folder' => 'gallery',
],
];

ImageUploader using Laravel configuration(Repository class):


Dependency injection 46

final class ImageUploader


{
/** @var ImageGuard */
private $imageGuard;

/** @var FileSystemManager */


private $fileSystemManager;

/** @var WrongImageUploadsListener */


private $listener;

/** @var Repository */


private $config;

public function __construct(


ImageGuard $imageGuard,
FileSystemManager $fileSystemManager,
WrongImageUploadsListener $listener,
Repository $config)
{
$this->imageGuard = $imageGuard;
$this->fileSystemManager = $fileSystemManager;
$this->listener = $listener;
$this->config = $config;
}

/**
* @param UploadedFile $file
* @param User $uploadedBy
* @param string $type
* @return bool|string
*/
public function upload(
UploadedFile $file,
User $uploadedBy,
string $type)
Dependency injection 47

{
$fileContent = $file->getContents();

$options = $this->config->get('image.' . $type);


if(Arr::get($options, 'check', true)) {

$weak = Arr::get($options, 'weak', false);

if(!$this->imageGuard->check($fileContent, $weak)){

if(Arr::get($options, 'ban', true)) {


$this->listener->handle($uploadedBy);
}

return false;
}
}

$fileName = $options['folder'] . 'some_unique_file_name.jpg';

$defaultDisk = $this->config->get('image.disk');

$this->fileSystemManager
->disk(Arr::get($options, 'disk', $defaultDisk))
->put($fileName, $fileContent);

return $fileName;
}
}

Well, the code looks not as clean as the “OOP” variant, but its configuration and
implementation are very simple. For the image uploading task I prefer this way, but
for other tasks with more complicated configurations or orchestrations, the “OOP”
way might be more optimal.
Dependency injection 48

Extending interfaces
Sometimes, we need to extend an interface with some method. In the Domain layer
chapter, I’ll need a multiple events dispatch feature in each method of service classes.
Laravel’s event dispatcher only has the single dispatch method:

interface Dispatcher
{
//...

/**
* Dispatch an event and call the listeners.
*
* @param string|object $event
* @param mixed $payload
* @param bool $halt
* @return array|null
*/
public function dispatch($event,
$payload = [], $halt = false);
}

I need only simple foreach:

foreach ($events as $event)


{
$this->dispatcher->dispatch($event);
}

But I don’t want to copy-paste it in each method of each service class. C# and Kotlin
language’s “extension method” feature solves this problem:
Dependency injection 49

namespace ExtensionMethods
{
public static class MyExtensions
{
public static void MultiDispatch(
this Dispatcher dispatcher, Event[] events)
{
foreach (var event in events) {
dispatcher.Dispatch(event);
}
}
}
}

Then, each class can use MultiDispatch method:

using ExtensionMethods;

//...

dispatcher.MultiDispatch(events);

PHP doesn’t have this feature. For your own interfaces, the new method can be
added to the interface and implemented in each implementor. In case of an abstract
class(instead of interface), the method can be added right there without touching
inheritors. That’s why I usually prefer abstract classes. For vendor’s interfaces, this
is not possible, so the usual solution is:
Dependency injection 50

use Illuminate\Contracts\Events\Dispatcher;

abstract class BaseService


{
/** @var Dispatcher */
private $dispatcher;

public function __construct(Dispatcher $dispatcher)


{
$this->dispatcher = $dispatcher;
}

protected function dispatchEvents(array $events)


{
foreach ($events as $event)
{
$this->dispatcher->dispatch($event);
}
}
}

final class SomeService extends BaseService


{
public function __construct(..., Dispatcher $dispatcher)
{
parent::__construct($dispatcher);
//...
}

public function someMethod()


{
//...

$this->dispatchEvents($events);
}
}
Dependency injection 51

Using inheritance just to extend functionality is not a good idea. Constructors


become more complicated with parent:: calls. Extending another interface will
consequence changing all constructors.
Creating a new interface is a more natural solution. Service classes need only one
multiDispatch method from the dispatcher, so I can just make a new interface:

interface MultiDispatcher
{
public function multiDispatch(array $events);
}

and implement it:

use Illuminate\Contracts\Events\Dispatcher;

final class LaravelMultiDispatcher implements MultiDispatcher


{
/** @var Dispatcher */
private $dispatcher;

public function __construct(Dispatcher $dispatcher)


{
$this->dispatcher = $dispatcher;
}

public function multiDispatch(array $events)


{
foreach($events as $event)
{
$this->dispatcher->dispatch($event);
}
}
}

class AppServiceProvider extends ServiceProvider


Dependency injection 52

{
public function boot()
{
$this->app->bind(
MultiDispatcher::class,
LaravelMultiDispatcher::class);
}
}

BaseService class can be deleted, and service classes will just use this new interface:

final class SomeService


{
/** @var MultiDispatcher */
private $dispatcher;

public function __construct(..., MultiDispatcher $dispatcher)


{
//...
$this->dispatcher = $dispatcher;
}

public function someMethod()


{
//...

$this->dispatcher->multiDispatch($events);
}
}

As a bonus, now I can switch from the Laravel events engine to another, just by
another implementation of the MultiDispatcher interface.
When clients want to use the full interface, just with a new method, a new interface
can extend the base one:
Dependency injection 53

interface MultiDispatcher extends Dispatcher


{
public function multiDispatch(array $events);
}

final class LaravelMultiDispatcher


implements MultiDispatcher
{
/** @var Dispatcher */
private $dispatcher;

public function __construct(Dispatcher $dispatcher)


{
$this->dispatcher = $dispatcher;
}

public function multiDispatch(array $events)


{
foreach($events as $event)
{
$this->dispatcher->dispatch($event);
}
}

public function listen($events, $listener)


{
$this->dispatcher->listen($events, $listener);
}

public function dispatch(


$event, $payload = [], $halt = false)
{
$this->dispatcher->dispatch($event, $payload, $halt);
}

// Other Dispatcher methods


Dependency injection 54

For big interfaces, it might be annoying to delegate each method there. Some IDEs
for other languages(like C#) have commands to do it automatically. I hope PHP IDEs
will implement that, too.

Traits
PHP traits are the magical way to “inject” dependencies for free. They are very
powerful: they can access private fields of the main class and add new public and
even private methods there. I don’t like them, because they are part of PHP dark
magic, powerful and dangerous. I use them in unit test classes, because there is no
good reason to implement the Dependency Injection pattern there, but avoid doing
it in main application code. Traits are not OOP, so every case with them can be
implemented using OOP.

Traits extend interfaces


Multi dispatcher issue can be solved with traits:

trait MultiDispatch
{
public function multiDispatch(array $events)
{
foreach($events as $event)
{
$this->dispatcher->dispatch($event);
}
}
}

final class SomeService


{
use MultiDispatch;
Dependency injection 55

/** @var Dispatcher */


private $dispatcher;

public function __construct(..., Dispatcher $dispatcher)


{
//...
$this->dispatcher = $dispatcher;
}

public function someMethod()


{
//...

$this->multiDispatch($events);
}
}

The MultiDispatch trait assumes that the host class has a dispatcher field of the
Dispatcher class. It is better to not make these kinds of implicit dependencies. A
solution with the MultiDispatcher interface is more convenient and explicit.

Traits as partial classes


C# language has the partial classes feature. It can be used when a class becomes too
big and the developer wants to separate it for different files:
Dependency injection 56

// Foo.cs file
partial class Foo
{
public void bar(){}
}

// Foo2.cs file
partial class Foo
{
public void bar2(){}
}

var foo = new Foo();


foo.bar();
foo.bar2();

When the same happens in PHP, traits can be used as a partial class. Example from
Laravel:

class Request extends SymfonyRequest


implements Arrayable, ArrayAccess
{
use Concerns\InteractsWithContentTypes,
Concerns\InteractsWithFlashData,
Concerns\InteractsWithInput,

Big Request class has been separated for several traits. When some class “wants” to
be separated, it’s a very big hint: this class has too many responsibilities. Request
class can be composed by Session, RequestInput and other classes. Instead of
combining a class with traits, it is better to separate the responsibilities, create a class
for each of them, and use composition to use them together. Actually, the constructor
of Request class tells a lot:
Dependency injection 57

class Request
{
public function __construct(
array $query = array(),
array $request = array(),
array $attributes = array(),
array $cookies = array(),
array $files = array(),
array $server = array(),
$content = null)
{
//...
}

//...
}

Traits as a behavior
Eloquent traits, such as SoftDeletes, are examples of behavior traits. They change
the behavior of classes. Eloquent classes contain at least two responsibilities: storing
entity state and fetching/saving/deleting entities from a database, so behavior traits
can also change the way entities are fetched/saved/deleted and add new fields and
relations there. What about the configuration of a trait? There are a lot of possibilities.
SoftDeletes trait:
Dependency injection 58

trait SoftDeletes
{
/**
* Get the name of the "deleted at" column.
*
* @return string
*/
public function getDeletedAtColumn()
{
return defined('static::DELETED_AT')
? static::DELETED_AT
: 'deleted_at';
}
}

It searches the ‘DELETED_AT’ constant in the class. If there is no such constant, it


uses the default value. Even for this simple configuration, traits have to use magic
(defined function). Other Eloquent traits have more complicated configurations. I’ve
found one library and trait that has several configuration variables and methods. It
looks like:

trait DetectsChanges
{
//...
public function shouldLogUnguarded(): bool
{
if (! isset(static::$logUnguarded)) {
return false;
}
if (! static::$logUnguarded) {
return false;
}
if (in_array('*', $this->getGuarded())) {
return false;
}
return true;
Dependency injection 59

}
}

The same magic but with ‘isset’…


Just imagine:

class SomeModel
{
protected function behaviors(): array
{
return [
new SoftDeletes('another_deleted_at'),
DetectsChanges::create('column1', 'column2')
->onlyDirty()
->logUnguarded()
];
}
}

Explicit behaviors with a convenient configuration without polluting the host class.
Excellent! Fields and relations in Eloquent are virtual, so its implementations are also
possible.
Traits can also add public methods to the host class interface… I don’t think it’s a
good idea, but it’s also possible with something like macros, which are widely used
in Laravel. Active record implementations are impossible without magic, so traits and
behaviors will also contain it, but behaviors look more explicit, more object oriented,
and configuring them is much easier.
Of course, Eloquent behaviors exist only in my imagination. I tried to imagine a better
alternative and maybe I don’t understand some possible problems, but I definitely like
them more than traits.

Useless traits
Some traits are just useless. I found this one in Laravel sources:
Dependency injection 60

trait DispatchesJobs
{
protected function dispatch($job)
{
return app(Dispatcher::class)->dispatch($job);
}

public function dispatchNow($job)


{
return app(Dispatcher::class)->dispatchNow($job);
}
}

I don’t know why one method is protected and another one is public… I think it’s
just a mistake. It just adds the methods from Dispatcher to the host class.

class WantsToDispatchJobs
{
use DispatchesJobs;

public function someMethod()


{
//...

$this->dispatch(...);
}
}

Accessing this functionality without a trait is simpler in Laravel:


Dependency injection 61

class WantsToDispatchJobs
{
public function someMethod()
{
//...

\Bus::dispatch(...);

//or just

dispatch(...);
}
}

This “simplicity” is the main reason why people don’t use Dependency Injection in
PHP.

class WantsToDispatchJobs
{
/** @var Dispatcher */
private $dispatcher;

public function __construct(Dispatcher $dispatcher)


{
$this->dispatcher = $dispatcher;
}

public function someMethod()


{
//...

$this->dispatcher->dispatch(...);
}
}

This class is much simpler than previous examples, because the dependency on
Dispatcher is explicit, not implicit. It can be used in any application, which can create
Dependency injection 62

the Dispatcher instance. It doesn’t need a Laravel facade, trait or ‘dispatch’ function.
The only problem is bulky syntax with the constructor and private field. Even with
convenient auto-completion from the IDE, it looks a bit noisy. Kotlin language syntax
is much more elegant:

class WantsToDispatchJobs(val dispatcher: Dispatcher)


{
//somewhere...
dispatcher.dispatch(...);
}

PHP syntax is a big barrier to using DI. I hope something will reduce it in the future
(language syntax or IDE improvements).
After years of using and not using traits, I can say that developers create traits for
two reasons:

• to patch architectural problems


• to create architectural problems

It’s much better to fix the problems instead of patching them.

Static methods
I wrote that using a static method of another class creates a hard coded dependency,
but sometimes it’s okay. Example from previous chapter:
Dependency injection 63

final class CacheKeys


{
public static function getUserByIdKey(int $id)
{
return sprintf('user_%d_%d', $id, User::VERSION);
}

public static function getUserByEmailKey(string $email)


{
return sprintf('user_email_%s_%d',
$email,
User::VERSION);
}
//...
}

$key = CacheKeys::getUserByIdKey($id);

Cache keys are needed in at least two places: cache decorators for data fetching
classes and event listeners to catch entity changed events, and delete old ones from
the cache.
I could use this CacheKeys class by DI, but it doesn’t make sense. All these decorator
and listener classes form some structure which can be called a “cache module” for
this app. CacheKeys class will be a private part of this module. All other application
code shouldn’t know about it.
Dependency injection 64

Using static methods for these kinds of internal dependencies that don’t work with
the outside world(files, database, APIs) is normal practice.

Conclusion
One of the biggest advantages of using the Dependency Injection technique is the
explicit contract of each class. The public methods of this class fully describe what it
can do. The constructor parameters fully describe what this class needs to do its job.
In big, long-term projects it’s a big advantage: classes can be easily unit tested and
used in different conditions(with dependencies provided). All these magic methods,
like __call, Laravel facades and traits break this harmony.
However, I can’t imagine HTTP controllers outside Laravel applications and almost
nobody unit tests them. That’s why I use typical helper functions (redirect(), view())
and Laravel facades (Response, URL) there.
4. Painless refactoring
“Static” typing
Big, long-term projects should be regularly refactored to be in good shape. Extracting
methods and classes from another method or class. Renaming them, adding or
removing parameters. Switching from one (for example, deprecated) method or class
to another. Modern IDEs have a lot of tools that make refactoring easy, sometimes
fully automatic. However, it might not be easy in PHP.

public function publish($id)


{
$post = Post::find($id);
$post->publish();
}

// Or

public function makePublish($post)


{
$post->publish();
}

In both of these cases, the IDE can’t find out that Post::publish method was called.
Let’s try to add a parameter to this method.

public function publish(User $publisher)

Next, we have to find all publish method calls and add a publisher there. The IDE
can’t automatically find it, because of dynamic typing in PHP. So, we have to find all
Painless refactoring 66

‘publish’ words in the whole project and filter it(there can be a lot of other ‘publish’
methods or comments).
Next, let’s imagine the team found a non-email string in the email field of the
database. How did it happen? We need to find all User::$email field usages. But
how? The email field doesn’t even exist. It is virtual. We can try to find all ‘email’
words in our project, but it can be set like this:

$user = User::create($request->all());
//or
$user->fill($request->all())

All this automagic can sometimes make for big, unpleasant surprises. These bugs in
production should be found as soon as possible. Every minute counts.
After some hard refactorings and debugging, a new rule appeared in my projects:
make PHP as static as possible. The IDE should understand everything about each
method and field. Parameters type hinting and return type declaration should be
used with full power. When it’s not enough, phpDoc should help.

public function makePublish(Post $post)


{
$post->publish();
}

Features, like implicit binding in Laravel that allow one to get models automatically
can help, but remember about the danger I mentioned earlier!
Painless refactoring 67

public function publish(Post $post)


{
$post->publish();
}

// Or, with phpDoc

public function publish($id)


{
/**
* @var Post $post
*/
$post = Post::find($id);
$post->publish();
}

phpDoc can help in some complex cases:

/**
* @var Post[] $posts
*/
$posts = Post::all();
foreach($posts as $post)
{
$post->// Here IDE should autocomplete
// all methods and fields of Post class
}

Autocomplete is very convenient when you write code, but it’s even better. If IDE
autocompletes your code, it understands where these methods and fields are from
and it will find them when we ask.
If a function can return an object of some class, it should be declared as a return type
directly (starting from PHP 7) or in the @return tag of the function’s phpDoc:
Painless refactoring 68

public function getPost($id): Post


{
//...
}

/**
* @return Post[] | Collection
*/
public function getPostsBySomeCriteria(...)
{
return Post::where(...)->get();
}

While discussing that, I’ve been asked a lot of questions, like: “Why are you making
Java from PHP?” I’m not making Java from PHP, I’m just making very small
comments or type hinting just to have autocomplete now and a huge help with
refactoring and debugging in the future. Even for smaller projects it might be super
useful.

Templates
Nowadays, more and more projects are becoming API-only. Still, some of them use
template engines to generate HTML. There are a lot of method and field calls, too. A
usual view call in Laravel:

return view('posts.create', [
'author' => \Auth::user(),
'categories' => Category::all(),
]);

It looks like a function call. Compare with this pseudo-code:


Painless refactoring 69

/**
* @param User $author
* @param Category[] | Collection $categories
*/
function showPostCreateView(User $author, $categories): string
{
//
}

return showPostCreateView(\Auth::user(), Category::all());

So, we need the view parameters to be described. It’s easy when templates are php
files - phpDoc works as usual. But it is not easy for template engines like Blade and
depends on the IDE. I use PhpStorm, so can only talk about this IDE. For Blade
templates, phpDoc support is also implemented:

<?php
/**
* @var \App\Models\User $author
* @var \App\Models\Category[] $categories
*/
?>

@foreach($categories as $category)
{{$category->//Category class fields and methods autocomplete}}
@endforeach

I know, it may look very weird and seem like a useless waste of time for you. But
after all of this static typing, my code becomes much more flexible. I can, for example,
automatically rename methods. Each refactoring brings minimal pain.

Model fields
Using of php magic methods(__get, __set, __call, etc.) is highly discouraged. It will
be hard to find all their usages in the project. If you use them, phpDocs should be
added to the class. For example, a little Eloquent model class:
Painless refactoring 70

class User extends Model


{
public function roles()
{
return $this->hasMany(Role::class);
}
}

This class has virtual fields, based on fields of ‘users’ table and also the ‘roles’ virtual
field as a relationship. There are lot of tools which help with that. For Laravel
Eloquent, I use the laravel-ide-helper package. Just one command in console and
it generates super useful phpDocs for each Eloquent class:

/**
* App\User
*
* @property int $id
* @property string $name
* @property string $email
* @property-read Collection|\App\Role[] $roles
* @method static Builder|\App\User whereEmail($value)
* @method static Builder|\App\User whereId($value)
* @method static Builder|\App\User whereName($value)
* @mixin \Eloquent
*/
class User extends Model
{
public function roles()
{
return $this->hasMany(Role::class);
}
}

$user = new User();


$user->// Here IDE will autocomplete fields!
Painless refactoring 71

This command should be run every time after the database or model relations change.
Let’s return to our example:

public function store(Request $request, ImageUploader $imageUploader)


{
$this->validate($request, [
'email' => 'required|email',
'name' => 'required',
'avatar' => 'required|image',
]);

$avatarFileName = ...;
$imageUploader->upload($avatarFileName, $request->file('avatar'));

$user = new User($request->except('avatar'));


$user->avatarUrl = $avatarFileName;

if(!$user->save()) {
return redirect()->back()->withMessage('...');
}

\Email::send($user->email, 'Hi email');

return redirect()->route('users');
}

User entity creation looks a bit weird. Before, it was like this:

User::create($request->all());

Then we had to change it, because the avatar field should not be used directly.
Painless refactoring 72

$user = new User($request->except('avatar'));


$user->avatarUrl = $avatarFileName;

It’s not only looking weird, but also vulnerable. This store method is usual for user
registration. Then, for example, the ‘admin’ field will be added to the ‘users’ table.
It will be accessible in the admin area to change to each user. But a hacker can just
add something like this to the register form:

<input type="hidden" name="admin" value="1">

And he will be an admin right after registration! For these reasons, some experts
suggest to use:

$request->only(['email', 'name']);

But, if we have to list all of them, it may be better to set the values to name and email
fields directly:

$user = new User();


$user->email = $request['email'];
$user->name = $request['name'];
$user->avatarUrl = $avatarFileName;

Now IDE can find this email property using.


“What if I have 50 fields?” First, maybe the UI should be changed :) 50 fields is too
many. If not, later in this book I’ll show how fields can be composed to something
more suitable and convenient.
Well, we made our code more convenient to refactoring and debugging. This “static
typing” isn’t required, but it is so helpful. You should at least try it.
5. Application layer
Let’s continue with our example as the application grows. New fields are added to
the registration form: birth date and a checkbox about subscribing to our newsletters.

public function store(


Request $request,
ImageUploader $imageUploader)
{
$this->validate($request, [
'email' => 'required|email',
'name' => 'required',
'avatar' => 'required|image',
'birthDate' => 'required|date',
]);

$avatarFileName = ...;
$imageUploader->upload(
$avatarFileName, $request->file('avatar'));

$user = new User();


$user->email = $request['email'];
$user->name = $request['name'];
$user->avatarUrl = $avatarFileName;
$user->subscribed = $request->has('subscribed');
$user->birthDate = new DateTime($request['birthDate']);

if(!$user->save()) {
return redirect()->back()->withMessage('...');
}

\Email::send($user->email, 'Hi email');


Application layer 74

return redirect()->route('users');
}

The application continues growing. Some API appears and user registration should be
implemented there, too. Also, some users importing from console command should
be implemented. And a Facebook bot! Users have to register there, too. As you see,
there are lots of interfaces that want to make some actions with our app. Not only
user registration, but almost all other actions that are accessible in the traditional
web interface. The most natural solution here is to extract the common logic with
some entity (User, in this example) to a new class. This kind of class is usually called
a “service class”:

final class UserService


{
public function getById(...): User;
public function getByEmail(...): User;

public function create(...);


public function ban(...);
...
}

But multiple interfaces (API, Web, etc.) are not the only reason to extract service
classes. Action methods can grow, and usually there will be two big parts: the
business logic and web logic. Pseudo-example:
Application layer 75

public function doSomething(Request $request, $id)


{
$entity = Entity::find($id);

if(!$entity) {
abort(404);
}

if(count($request['options']) < 2) {
return redirect()->back()->withMessage('...');
}

if($entity->something) {
return redirect()->back()->withMessage('...');
}

\Db::transaction(function () use ($request, $entity) {


$entity->someProperty = $request['someProperty'];

foreach($request['options'] as $option) {
//...
}

$entity->save();
});

return redirect()->...
}

This method has at least 2 different responsibilities - Http request/response process-


ing and business logic. Each time the developer changes the http part, he has to
read a lot of business code and vice versa. This code is hard to debug and maintain.
Refactoring is also difficult. So, extracting business logic to service classes is a good
solution here, too.
Application layer 76

Request data passing


Lets start to create our UserService. The first problem is how to provide data there.
Some actions don’t need much data, like the remove post action needing only the post
id as a parameter, but the user create action needs a lot. We can’t use web Request
class; it’s accessible only in web interfaces. So, let’s try simple arrays:

final class UserService


{
/** @var ImageUploader */
private $imageUploader;

/** @var EmailSender */


private $emailSender;

public function __construct(


ImageUploader $imageUploader, EmailSender $emailSender)
{
$this->imageUploader = $imageUploader;
$this->emailSender = $emailSender;
}

public function create(array $request)


{
$avatarFileName = ...;
$this->imageUploader->upload(
$avatarFileName, $request['avatar']);

$user = new User();


$user->email = $request['email'];
$user->name = $request['name'];
$user->avatarUrl = $avatarFileName;
$user->subscribed = isset($request['subscribed']);
$user->birthDate = new DateTime($request['birthDate']);
Application layer 77

if(!$user->save()) {
return false;
}

$this->emailSender->send($user->email, 'Hi email');

return true;
}
}

public function store(Request $request, UserService $userService)


{
$this->validate($request, [
'email' => 'required|email',
'name' => 'required',
'avatar' => 'required|image',
'birthDate' => 'required|date',
]);

if(!$userService->create($request->all())) {
return redirect()->back()->withMessage('...');
}

return redirect()->route('users');
}

I just extracted logic without any other refactorings and here I see some problems.
When we try to use it from console, for example, there will be code like this:
Application layer 78

$data = [
'email' => $email,
'name' => $name,
'avatar' => $avatarFile,
'birthDate' => $birthDate->format('Y-m-d'),
];

if($subscribed) {
$data['subscribed'] = true;
}

$userService->create($data);

Looks a bit weird. By extracting request data, we moved HTML forms logic to our
service class. Boolean field values are checked by its existence in the array. Datetime
fields(actually, all field types) are parsed from strings. When we try to use this logic
in another environment (API, Bots, Console) it becomes very inconvenient. We need
another way to provide data to service classes. The most common pattern to transfer
data between layers is Data Transfer Object(DTO).

final class UserCreateDto


{
/** @var string */
private $email;

/** @var DateTime */


private $birthDate;

/** @var bool */


private $subscribed;

public function __construct(


string $email, DateTime $birthDate, bool $subscribed)
{
$this->email = $email;
$this->birthDate = $birthDate;
Application layer 79

$this->subscribed = $subscribed;
}

public function getEmail(): string


{
return $this->email;
}

public function getBirthDate(): DateTime


{
return $this->birthDate;
}

public function isSubscribed(): bool


{
return $this->subscribed;
}
}

Very often I hear something like: “I don’t want to create a whole class just to provide
data. Arrays are okay!” Maybe it’s true, but to create a class like UserCreateDto in
a modern IDE like PhpStorm is: type the name of it in the “Create class” dialog, hot
key to create a constructor(Alt+Ins > Constructor…), type fields with type hinting in
constructor parameters, hot key to create fields from constructor parameters and fill
them with values(Alt+Enter on constructor parameters > Initialize fields) and then
hot key to create getters automatically (Alt+Ins in class body > Getters…). Less than
30 seconds and we have a convenient class with full type hinting.
Application layer 80

final class UserService


{
//...

public function create(UserCreateDto $request)


{
$avatarFileName = ...;
$this->imageUploader->upload(
$avatarFileName, $request->getAvatarFile());

$user = new User();


$user->email = $request->getEmail();
$user->name = $request->getName();
$user->avatarUrl = $avatarFileName;
$user->subscribed = $request->isSubscribed();
$user->birthDate = $request->getBirthDate();

if(!$user->save()) {
return false;
}

$this->emailSender->send($user->email, 'Hi email');

return true;
}
}

public function store(Request $request, UserService $userService)


{
$this->validate($request, [
'email' => 'required|email',
'name' => 'required',
'avatar' => 'required|image',
'birthDate' => 'required|date',
]);
Application layer 81

$dto = new UserCreateDto(


$request['email'],
new DateTime($request['birthDate']),
$request->has('subscribed'));

if(!$userService->create($dto)) {
return redirect()->back()->withMessage('...');
}

return redirect()->route('users');
}

Now it looks canonical. The service class gets pure DTO and executes the action. But
the controller action code looks too noisy. UserCreateDto constructor can be very
big, and maybe we will have to use the Builder pattern or just make the fields public
instead of private.
The request validation and DTO object creation can be moved to some class which
will consolidate these actions. Laravel has a suitable form request class for that:

final class UserCreateRequest extends FormRequest


{
public function rules()
{
return [
'email' => 'required|email',
'name' => 'required',
'avatar' => 'required|image',
'birthDate' => 'required|date',
];
}

public function authorize()


{
return true;
}
Application layer 82

public function getDto(): UserCreateDto


{
return new UserCreateDto(
$this->get('email'),
new DateTime($this->get('birthDate')),
$this->has('subscribed'));
}
}

final class UserController extends Controller


{
public function store(
UserCreateRequest $request, UserService $userService)
{
if(!$userService->create($request->getDto())) {
return redirect()->back()->withMessage('...');
}

return redirect()->route('users');
}
}

If some class asks FormRequest class as a dependency, Laravel creates it and makes
a validation automatically. In case of invalid data store action won’t be executed,
and that’s why store method can be sure that data in UserCreateRequest object is
always valid.
The Laravel form request class has a rules method to validate request data and an
authorize method to authorize the request. Form request is definitely the wrong
place to authorize itself. Authorization usually requires some context, like which
entity by which user… It can be retrieved in the authorize method, but it is better to
do it in the service class. There will be all needed data. So, it is better to create a base
FormRequest class with the authorize method returning true and then forget about
this method.
Application layer 83

Work with database


Let me start from a simple example:

class PostController
{
public function publish($id, PostService $postService)
{
$post = Post::find($id);

if(!$post) {
abort(404);
}

if(!$postService->publish($post)) {
return redirect()->back()->withMessage('...');
}

return redirect()->route('posts');
}
}

final class PostService


{
public function publish(Post $post)
{
$post->published = true;
return $post->save();
}
}

Publishing post is one of the simplest examples of a non-CRUD action and I’ll use it
a lot. Everything seems okay, but when we try to use it from another interface, like
console, we will have to implement entity fetching from the database again.
Application layer 84

public function handle(PostService $postService)


{
$post = Post::find(...);

if(!$post) {
$this->error(...);
return;
}

if(!$postService->publish($post)) {
$this->error(...);
} else {
$this->info(...);
}
}

It’s an example of violating the Single Responsibility principle and high coupling.
Every part of the application works with the database. Any database-related change
will result in changes to the whole app. Sometimes I see strange solutions using
services getById(or just get) method:

class PostController
{
public function publish($id, PostService $postService)
{
$post = $postService->getById($id);

if(!$postService->publish($post)) {
return redirect()->back()->withMessage('...');
}

return redirect()->route('posts');
}
}

This code gets the post entity but everything it does with this entity just providing it
Application layer 85

to another service method. Controller action publish doesn’t need an entity. It just
should ask service to publish this post. The most simple and logical way is:

class PostController
{
public function publish($id, PostService $postService)
{
if(!$postService->publish($id)) {
return redirect()->back()->withMessage('...');
}

return redirect()->route('posts');
}
}

final class PostService


{
public function publish(int $id)
{
$post = Post::find($id);

if(!$post) {
return false;
}

$post->published = true;

return $post->save();
}
}

One of the most important pro’s of extracting service classes is consolidating work
with business layers and infrastructure, including storages like database and files,
in one place and leaving the Web, API, Console and other interfaces to work only
with their own responsibilities. The Web part should only prepare requests to the
service class and show the results to user. The same is true for the other interfaces.
Application layer 86

It’s an SRP for layers. Layers? Yes. All these service classes, which hide all application
logic inside and provide convenient methods to Web, API and other parts, form some
structure, which has a lot of names:

• Service layer, because of service classes.


• Application layer, because it contains whole application logic, leaving other
jobs such as web request processing to other parts.
• GRASP patterns call this a Controllers layer, assuming service classes as a
controller.
• there might be some other names.

I like the “controllers layer” name, but it makes sense if this layer only makes a
control. For our current state, though, this layer makes almost everything. Service
layer also sounds good, but in some projects there are no service classes (see next
section). Application layer term is also used in the telecommunications industry
and can lead to some misunderstandings. For this book I chose Application layer.
Because I can.

Service classes or command classes


When an entity has a rich logic and a lot of actions, a service class for this entity can
be very big. Every action also wants different dependencies. One action wants email
sender and file storage. Another one - some API wrapper. The number of service
class constructor parameters grow very quickly and there are a lot of dependencies
used only by one-two actions. Usually it happens very quickly in projects, that’s
why I usually create a class for each application layer action. As far as I know,
there is no standard for how these classes should be named. I saw action classes
with a UseCase suffix, like CreatePostUseCase. Also the Action suffix is popular:
CreatePostAction. I usually use the Command suffix: CreatePostCommand, Pub-
lishPostCommand, DeletePostCommand, etc.
Application layer 87

final class PublishPostCommand


{
public function execute($id)
{
//...
}
}

In Command bus pattern, the Command suffix is usually used only for DTO’s, and
the CommandHandler suffix is for classes that handle the commands.

final class ChangeUserPasswordCommand


{
//...
}

final class ChangeUserPasswordCommandHandler


{
public function handle(
ChangeUserPasswordCommand $command)
{
//...
}
}

// or in case of one class to handle


// many commands for some context(User)

final class UserCommandHandler


{
public function handleChangePassword(
ChangeUserPasswordCommand $command)
{
//...
}
}
Application layer 88

A little remark about long class names. “ChangeUserPasswordCommandHandler -


ugh! Such a long name! I don’t want to type it each time!” It will be fully typed only
once - during class creation. Every other time the IDE’s auto-complete functionality
will help you. A good, fully understandable name is much more important. You
can ask any developer “What should the ChangeUserPasswordCommandHandler
class do?” Some of them will say “Handle change user password command”. Others:
“Change user’s password”. Each of them more or less understands what happens
inside this class. That means developers won’t waste time trying to understand these
things. It’s much more important than a few seconds for typing.
I’ll use *Service classes for examples in this book to make them a bit more under-
standable.
6. Error handling
The C language, whose syntax was a base for a lot of modern languages, has a simple
error handling convention. If a function should return something and can’t do it for
some reason, it returns null. If a function should do something and everything is
okay, it returns 0, otherwise -1, or some error code. A lot of PHP developers like
this simplicity too! Functions which should return a value usually return null if they
can’t. Action functions usually return boolean values. True for success and false for
failure. The code looks like this:

final class ChangeUserPasswordDto


{
private $userId;
private $oldPassword;
private $newPassword;

public function __construct(


int $userId, string $oldPassword, string $newPassword)
{
$this->userId = $userId;
$this->oldPassword = $oldPassword;
$this->newPassword = $newPassword;
}

public function getUserId(): int


{
return $this->userId;
}

public function getOldPassword(): string


{
return $this->oldPassword;
Error handling 90

public function getNewPassword(): string


{
return $this->newPassword;
}
}

final class UserService


{
public function changePassword(
ChangeUserPasswordDto $command): bool
{
$user = User::find($command->getUserId());
if($user === null) {
return false; // user not found
}

if(!password_verify($command->getOldPassword(),
$user->password)) {
return false; // old password is not valid
}

$user->password = password_hash($command->getNewPassword());
return $user->save();
}
}

final class UserController


{
public function changePassword(UserService $service,
ChangeUserPasswordRequest $request)
{
if($service->changePassword($request->getDto())) {
// return success web response
} else {
Error handling 91

// return failure web response


}
}
}

Well, at least it works. But what if a user wants to know why his response was a
failure? There are comments with needed messages but they are useless in runtime.
Seems we need something more informative than a boolean value. Let’s try to
implement something like this:

final class FunctionResult


{
/** @var bool */
public $success;

/** @var mixed */


public $returnValue;

/** @var string */


public $errorMessage;

private function __construct() {}

public static function success(


$returnValue = null): FunctionResult
{
$result = new self();
$result->success = true;
$result->returnValue = $returnValue;

return $result;
}

public static function error(


string $errorMessage): FunctionResult
{
Error handling 92

$result = new self();


$result->success = false;
$result->errorMessage = $errorMessage;

return $result;
}
}

I created a class for function results. The constructor of this class is private, so its
objects can be created only by static factory methods FunctionResult::success and
FunctionResult::error. It’s a simple trick called “named” constructors.

return FunctionResult::error("Something is wrong");

Looks much more natural and more informative than

return new FunctionResult(false, null, "Something is wrong");

Our code will look like this:

class UserService
{
public function changePassword(
ChangeUserPasswordDto $command): FunctionResult
{
$user = User::find($command->getUserId());
if($user === null) {
return FunctionResult::error("User was not found");
}

if(!password_verify($command->getOldPassword(),
$user->password)) {
return FunctionResult::error("Old password isn't valid");
}
Error handling 93

$user->password = password_hash($command->getNewPassword());

$databaseSaveResult = $user->save();

if(!$databaseSaveResult->success) {
return FunctionResult::error("Database error");
}

return FunctionResult::success();
}
}

final class UserController


{
public function changePassword(UserService $service,
ChangeUserPasswordRequest $request)
{
$result = $service->changePassword($request->getDto());

if($result->success) {
// return success web response
} else {
// return failure web response
// with $result->errorMessage text
}
}
}

So, almost every function(even Eloquent’s save() method in my imaginary world)


returns a FunctionResult object with a detailed result of how this function executed.
When I was showing this example in one of my seminars, one listener said: “It looks
so ugly! Maybe it’s better to use exceptions?”. I think you agree with him. Just let me
show a Go language example:
Error handling 94

f, err := os.Open("filename.ext")
if err != nil {
log.Fatal(err)
}
// do something with the open *File f

It handles errors almost the same way! There are no exceptions. At least this
error handling without exceptions works, but if we want to continue using a
FunctionResult class in a PHP app, we have to implement call stack and correct
logging in each error if-branch. The whole app will be full of if($result->success)
checks. Definitely not the code I like. I like clean code, code which only describes
what should be done, without checking correctness after each step. Actually, the
‘validate’ method call is clean, because it throws an exception if data is invalid. So,
let’s start to use them too.

Exceptions
When the user asks our app to do some action, like register a user or cancel an order,
the app can execute it successfully or not. If not, there are a ton of reasons why it
could go wrong. One of the best illustrations of them is HTTP status codes. There
are 2xx and 3xx codes for success request processing, like 200 okay or 302 Found. 4xx
and 5xx are used for failed requests, but they are different!

• 4xx are the client errors: 400 Bad Request, 401 Unauthorized, 403 Forbidden,
etc.
• 5xx are the server errors: 500 Internal Server Error, 503 Service Unavailable, etc.

So, all failed validation and authorization, not found entities, and trying to change
password without knowing the old one are client errors. An API unavailable, file
storage error, or database connection issues are the server errors.
There are two main ways how exceptions can be used:

1. Exceptions should be thrown only on the server errors. In cases like “old pass-
word is not valid”, the function should return something like FunctionResult
object.
Error handling 95

2. Exceptions should be thrown on both server and client errors.

The first way looks more natural, but passing errors to high levels(from some internal
functions to callers, from application layer to controllers) is not very convenient. The
second way has unified error handling and cleaner code. There is only one main
flow when request processing goes well: user should be fetched from database, the
password should match to requests old_password, then password should be changed
to requests ‘new_password’ and user entity successfully saved to database. Every
step out of this way throws an exception. Validation failed - exception, authorization
failed - exception, business logic failure - exception. But later we have to separate
client and server errors for correct response generation and logging.
It’s really hard to say which way is better. When the app only extracted the
application layer, the second way with exceptions is better. Code is much cleaner
this way. Every needed piece of information can be passed through the call stack.
But when the application grows and the Domain layer will also be extracted from
the Application layer, the exceptions can start to cause troubles. Some exceptions can
be thrown and if they won’t be caught in the needed level, they can be interpreted
incorrectly in a higher level. So, every internal call should be very accurate and
usually surrounded by a try catch structure.
Laravel throws an exception for 404 response, non-authorized(403) error, validation
check failed case, so I also chose the second way for this book. We will throw an
exception for every case when a requested action cannot be executed.
Let’s try to write code with exceptions:

class UserService
{
public function changePassword(
ChangeUserPasswordDto $command): void
{
$user = User::findOrFail($command->getUserId());

if(!password_verify($command->getOldPassword(),
$user->password)) {
throw new \Exception("Old password is not valid");
Error handling 96

$user->password = password_hash($command->getNewPassword());

$user->saveOrFail();
}
}

final class UserController


{
public function changePassword(UserService $service,
ChangeUserPasswordRequest $request)
{
try {
$service->changePassword($request->getDto());
} catch(\Throwable $e) {
// log error
// return failure web response with $e->getMessage();
}

// return success web response


}
}

As you see, even in this super simple example, UserService::changePassword


method code looks much cleaner. All cases outside of main flow just throw an
exception, which stops following execution and goes to a high level(controller).
Eloquent also has methods for this style of code: findOrFail(), firstOrFail() and some
other *OrFail() methods. But this code has several problems:

1. Exception::getMessage() is not a good message to show to a user. “Old


password is not valid” message is okay, but “Server Has Gone Away (error
2006)” is definitely not.
2. Each server exception should be logged. Small applications use log files. When
the application becomes more popular, exceptions can be thrown each second.
Some exceptions signal a problem in the code and have to be fixed immediately.
Error handling 97

Some exceptions are okay. Internet not ideal, API’s regularly answer with error
HTTP codes or just timeouts. Developers have to react only if the rate of
exceptions with some APIs becomes too frequent.
In that case, it is better to use special services to store the logs. These services
allows developers to group and work with exceptions more conveniently. Just
ask Google “error monitoring services” and it helps to find some of them. Big
companies build special solutions for storing and analyzing logs from all of
their servers. Some companies prefer to store client exceptions too, but storing,
for example, 404 errors looks strange for me (it may be stored in http server
log, but not in application logs). Anyway, we have to separate server and client
exceptions and process them differently.

Base exception class


The first step is to create an exception class for all business-related exceptions (like
“Old password is not valid”). PHP has a DomainException class that can be used for
it, but other code also could use it and it might become a big surprise. Especially if
we won’t log them. So, better to create our own class. Let say BusinessException.

class BusinessException extends \Exception


{
/**
* @var string
*/
private $userMessage;

public function __construct(string $userMessage)


{
$this->userMessage = $userMessage;
parent::__construct("Business exception");
}

public function getUserMessage(): string


{
return $this->userMessage;
Error handling 98

}
}

// Now password verification failed case will throw new exception

if(!password_verify($command->getOldPassword(), $user->password)) {
throw new BusinessException("Old password is not valid");
}

final class UserController


{
public function changePassword(UserService $service,
ChangeUserPasswordRequest $request)
{
try {
$service->changePassword($request->getDto());
} catch(BusinessException $e) {
// return failure web response
// (with 400 HTTP code)
// with $e->getUserMessage();
} catch(\Throwable $e) {
// log error

// return failure web response(with 500 HTTP code)


// with "Houston, we have a problem";
// Not with real exception message
}

// return success web response


}
}

This code catches BusinessException and shows the error message to the user. For
all other exceptions some “Inner exception” message will be shown to user and the
exception will be logged. This code works correctly. User (or API caller) sees correct
errors. Each error which should be logged will be logged. But this “catch” part will
Error handling 99

be repeated in each controller action. For all web actions it will be the same. For all
API actions, too. We can extract it to higher level.

Global handler
Laravel(and almost all other frameworks) has a global exceptions handler and it is the
best place to extract common “catch” logic. Laravel’s app/Exceptions/Handler.php
class has 2 close responsibilities: reporting and rendering exceptions.

namespace App\Exceptions;

class Handler extends ExceptionHandler


{
protected $dontReport = [
// This means BusinessException
// should not be reported,
// but it will be rendered
BusinessException::class,
];

public function report(Exception $e)


{
if ($this->shouldReport($e))
{
// Here is the best place to
// integrate external exception
// monitoring services
}

// This will log exception to log file, by default


parent::report($e);
}

public function render($request, Exception $e)


{
Error handling 100

if ($e instanceof BusinessException)


{
if($request->ajax())
{
$json = [
'success' => false,
'error' => $e->getUserMessage(),
];

return response()->json($json, 400);


}
else
{
return redirect()->back()
->withInput()
->withErrors([
'error' => trans($e->getUserMessage())]);
}
}

// Default rendering,
// like showing 404 page for 404 error,
// "Oops" page for 500 error, etc.
return parent::render($request, $e);
}
}

Simple example of Handler class. ‘report’ method can be used for some additional
reporting. “catch” part from controller actions was moved to ‘render’ method. Here
all BusinessException objects will be filtered and correct responses will be generated
both for web and API parts. For CLI custom errors rendering undocumented ren-
derForConsole($output, Exception $e) method might be overwritten in Handler
class. Now controller action code becomes much prettier:
Error handling 101

final class UserController


{
public function changePassword(UserService $service,
ChangeUserPasswordRequest $request)
{
$service->changePassword($request->getDto());

// return success web response


}
}

Checked and unchecked exceptions


Let’s take a look at the UserService::changePassword method. What kind of
exceptions can be thrown there?

• IlluminateDatabaseEloquentModelNotFoundException if there is no user


with this id
• IlluminateDatabaseQueryException if database query couldn’t be executed
• AppExceptionsBusinessException if old password is wrong
• TypeError if somewhere deep in the code function foo(SomeClass $x) will get
$x parameter value with another type
• Error if some $var->method() will be called when $var is null
• a lot of other possible exceptions

From the UserService::changePassword callee point of view, some of these excep-


tions, like Error, TypeError, QueryException are definitely out of context. Http
controller doesn’t know what to do with them. The only possible reaction - to show
some message, like “Something happened and I don’t know what to do!” But some of
them make sense for this controller. BusinessException means that something was
wrong with the logic inside and it has a special message for the user. Controller
definitely knows what to do if this exception was thrown. The same can be said
about ModelNotFoundException. Controller can show 404 error in that case. So, 2
types of errors:
Error handling 102

1. Errors which can be effectively processed by caller


2. Other errors

It will be good if the first set of errors will be caught right in the caller, because in
this place it has maximum information on how to handle this exception. If it will be
caught in higher layers it will be hard to react correctly. Let’s keep this in mind and
take a look at Java.

public class Foo


{
public void bar()
{
throw new Exception("test");
}
}

This code won’t even be compiled. Compiler’s message: “Error:(5, 9) java: unreported
exception java.lang.Exception; must be caught or declared to be thrown” There are
2 ways to fix that. Catch it:

public class Foo


{
public void bar()
{
try {
throw new Exception("test");
} catch(Exception e) {
// do something
}
}
}

Or declare this exception in method signature:


Error handling 103

public class Foo


{
public void bar() throws Exception
{
throw new Exception("test");
}
}

In that case, every method caller has to catch this exception or, again, describe it in
its own signature:

public class FooCaller


{
public void caller() throws Exception
{
(new Foo)->bar();
}

public void caller2()


{
try {
(new Foo)->bar();
} catch(Exception e) {
// do something
}
}
}

Of course, working like that with ALL exceptions can be very annoying. Java has
checked exceptions, which have to be declared or caught, and unchecked exceptions,
which can be thrown without any conditions. Take a look to the Java main exception
classes tree (PHP starting from 7 version has the same structure):
Error handling 104

Throwable(checked)
/ \
Error(unchecked) Exception(checked)
\
RuntimeException(unchecked)

All Throwable and Exception derived classes become checked exceptions. Except
Error and RuntimeException and all their derived classes.

public class File


{
public String getCanonicalPath() throws IOException {
//...
}
}

What does the getCanonicalPath method signature say to the developer? It doesn’t
have any parameters. Returns String object. Throws checked exception IOException
and maybe some unchecked exceptions.
Returning to our 2 error types:

1. Errors which can be effectively processed by caller


2. Other errors

Checked exceptions are created for the first error type. Unchecked for second. Caller
has to do something with a checked exception and this strictness helps to make the
code which processes exceptions as correct as possible.
Well, Java has this feature. PHP doesn’t. Why am I still talking about it? The IDE I
use, PHPStorm, imitates this Java behaviour.
Error handling 105

class Foo
{
public function bar()
{
throw new Exception();
}
}

PHPStorm will highlight ‘throw new Exception();’ with warning: ‘Unhandled Ex-
ception’. The same two ways to remove this warning:

1. Catch the exception


2. Describe it in @throws tag on methods phpDoc:

class Foo
{
/**
* @throws Exception
*/
public function bar()
{
throw new Exception();
}
}

The unchecked exception classes list is configurable. By default there are: Error,
RuntimeException and LogicException classes. Throwing them and their derived
classes doesn’t create any warnings.
With all this information we now can decide how to build our exception classes. I
definitely want to inform the UserService::changePassword caller about:

1. ModelNotFoundException, when needed user wasn’t found


2. BusinessException, this exception sends the error message to the user and must
be processed correctly. All other exceptions can be processed later.
So, in an ideal world:
Error handling 106

class ModelNotFoundException extends \Exception


{...}

class BusinessException extends \Exception


{...}

final class UserService


{
/**
* @param ChangeUserPasswordDto $command
* @throws ModelNotFoundException
* @throws BusinessException
*/
public function changePassword(
ChangeUserPasswordDto $command): void
{...}
}

But we already moved all exceptions handling to the Handler class and now we have
to copy @throws tags in controller actions:

final class UserController


{
/**
* @param $userId
* @param UserService $service
* @param Request $request
* @throws ModelNotFoundException
* @throws BusinessException
*/
public function changePassword(UserService $service,
ChangeUserPasswordRequest $request)
{
$service->changePassword($request->getDto());

// return success web response


Error handling 107

}
}

Not very convenient. Even if PHPStorm can auto-create all these phpDocs. Returning
to our non-ideal world: Laravel’s ModelNotFoundException already inherited from
RuntimeException. So, by default it is unchecked. It’s reasonable, because Laravel
already has this exception processing deep in frameworks Handler class. In our
current state, it may be better also to do the same trade-off:

class BusinessException extends \RuntimeException


{...}

and forget about @throws tag assuming all BusinessException exceptions will be
processed in the global Handler class.
Actually, this is one of the reasons why modern languages don’t have a checked
exceptions feature and most Java developers don’t use them. Another reason:
some libraries just make “throws Exception” in their methods. “throws Exception”
statement doesn’t give any useful information. It just forces client code to catch
exceptions or repeat this useless “throws Exception” in its own signature.
I’ll return to exceptions in the Domain layer chapter, when this way with unchecked
exceptions will become not very convenient.

Conclusion
Function or method returning more than one possible type, nullable or boolean
result(ok/not ok) makes a callee code more dirty. Each callee has to check the
result(‘!== null’ or ‘if($result)’) right after call. Code with exceptions looks cleaner:
Error handling 108

// Without exception
$user = User::find($command->getUserId());
if($user === null) {
// process the error
}

$user->doSomething();

// With exception
$user = User::findOrFail($command->getUserId());
$user->doSomething();

On the other hand, using something like FunctionResult objects gives much more
control to the developer. For example, careless findOrFail will show 404 error, instead
of the correct error message. Exceptions should be used very accurately.
7. Validation
“…But, now you come to me, and you say: “Don Corleone, give me justice.” But
you don’t ask with respect. You don’t offer friendship…”

Database related validation


As usual, I’m starting a new chapter with the code example containing all of the
practices described in the book up to this point. Create post use case:

class PostController extends Controller


{
public function create(Request $request, PostService $service)
{
$this->validate($request, [
'category_id' => 'required|exists:categories',
'title' => 'required',
'body' => 'required',
]);

$service->create(/* DTO */);

//...
}
}

class PostService
{
Validation 110

public function create(CreatePostDto $dto)


{
$post = new Post();
$post->category_id = $dto->getCategoryId();
$post->title = $dto->getTitle();
$post->body = $dto->getBody();

$post->saveOrFail();
}
}

Later, a “soft delete” feature will be added to the Category model. The soft delete
pattern suggests to mark rows as deleted by special field instead of actually deleting
by DELETE SQL command. In Laravel, it can be implemented simply by adding the
SoftDeletes trait to the Category class. Other parts of application did not change
and it works as usual. But our validation rule for the category_id field is broken!
It accepts deleted categories. We have to change it (and all other ‘category_id’ field
validations in the entire app).

$this->validate($request, [
'category_id' => [
'required|exists:categories,id,deleted_at,null',
],
'title' => 'required',
'body' => 'required',
]);

New requirement: add ‘archived’ field to Category model and don’t allow post
creation in archived categories. How can this be implemented? Changing the
validation rules again? Add $query->where(‘archived’, 0); there? But the HTTP part
of the application haven’t changed! We are just changing our business logic (with
archived field) and database storing (with soft delete). Why do all of these changes
affect HTTP requests? It’s another example of high coupling. As you remember, we
moved all work with database to the application layer, but validation rules were
forgotten.
Validation 111

Let’s separate validations:

$this->validate($request, [
'category_id' => 'required',
'title' => 'required',
'body' => 'required',
]);

class PostService
{
public function create(CreatePostDto $dto)
{
$category = Category::find($dto->getCategoryId());

if($category === null) {


// throw "Category not found" exception
}

if($category->archived) {
// throw "Category archived" exception
Validation 112

$post = new Post();


$post->category_id = $category->id;
$post->title = $dto->getTitle();
$post->body = $dto->getBody();

$post->saveOrFail();
}
}

Okay, now validation is in its correct place. PostService::create doesn’t trust such
important validation to high-level callers and validates by itself. As a bonus, the
application now has more informative error messages.

Two levels of validation


In the previous example, we have some validation both in the request class and in
the application layer. I also said that PostService::create doesn’t trust validation to
callers, but it still trusts. It makes:
Validation 113

$post->title = $request->getTitle();

and here we can’t be 100% sure that getTitle() will return a non-empty string! Yes, it
will be checked with ‘required’ validation, but it’s so far away in another layer of our
app. PostService::create can be called from another place and this validation can be
forgotten there. Let’s use a user registration use case as a better example of this:

class RegisterUserDto
{
/** @var string */
private $name;

/** @var string */


private $email;

/** @var DateTime */


private $birthDate;

public function __construct(


string $name, string $email, DateTime $birthDate)
{
$this->name = $name;
$this->email = $email;
$this->birthDate = $birthDate;
}

public function getName(): string


{
return $this->name;
}

public function getEmail(): string


{
return $this->email;
}
Validation 114

public function getBirthDate(): DateTime


{
return $this->birthDate;
}
}

class UserService
{
public function register(RegisterUserDto $request)
{
$existingUser = User::whereEmail($request->getEmail())
->first();

if($existingUser !== null) {


throw new UserWithThisEmailAlreadyExists(...);
}

$user = new User();


$user->name = $request->getName();
$user->email = $request->getEmail();
$user->birthDate = $request->getBirthDate();

$user->saveOrFail();
}
}

By moving to DTO, we had to forget about the web request validation. Yes, there
is a validation with ‘required’ and ‘email’ fields in the web interface, but another
interface, like bot, can provide corrupted data.

$userService->register(new RegisterUserDto('', '', new DateTime()));

CreateUserCommand can’t be sure that getName() will return a non-empty string


and getEmail() will return a string with a correct email. What to do? Some duplicated
validation can be used in command:
Validation 115

class UserService
{
public function register(RegisterUserDto $request)
{
if(empty($request->getName())) {
throw //
}

if(!filter_var($request->getEmail(),
FILTER_VALIDATE_EMAIL)) {
throw //
}

//...
}
}

The same validation can be used in DTO’s constructor, but there will be a lot of
duplicated validation. There will be a lot of places in the application with name and
email values. I can suggest two ways to solve this problem.

Validation by annotations
Symfony framework has a great component for validation: symfony/validator.
To use it not in symfony (today in 2019), you have to install symfony/validator,
doctrine/annotations and doctrine/cache composer packages and make some
initialization for the Annotation loader. Rewriting our RegisterUserDto:
Validation 116

use Symfony\Component\Validator\Constraints as Assert;

class RegisterUserDto
{
/**
* @Assert\NotBlank()
* @var string
*/
private $name;

/**
* @Assert\NotBlank()
* @Assert\Email()
* @var string
*/
private $email;

/**
* @Assert\NotNull()
* @var DateTime
*/
private $birthDate;

// Constructor and getters are the same


}

Then, let’s bind ValidatorInterface instance:


Validation 117

$container->bind(
\Symfony\Component\Validator\Validator\ValidatorInterface::class,
function() {
return \Symfony\Component\Validator\Validation
::createValidatorBuilder()
->enableAnnotationMapping()
->getValidator();
});

Now, we can use it in the service class:

class UserService
{
/** @var ValidatorInterface */
private $validator;

public function __construct(ValidatorInterface $validator)


{
$this->validator = $validator;
}

public function register(RegisterUserDto $dto)


{
$violations = $this->validator->validate($dto);

if (count($violations) > 0) {
throw new ValidationException($violations);
}

$existingUser = User::whereEmail($dto->getEmail())->first();

if($existingUser !== null) {


throw new UserWithThisEmailAlreadyExists(...);
}

$user = new User();


Validation 118

$user->name = $dto->getName();
$user->email = $dto->getEmail();
$user->birthDate = $dto->getBirthDate();

$user->saveOrFail();
}
}

ValidatorInterface::validate method returns the list of violations. If it’s empty -


everything is okay. Otherwise, the ValidationException will be thrown. Using this
explicit validation, the application layer action can be sure about data validity.
Also, as a big advantage, the Web and API layers can remove their own validation.
Anyway, all request data will be validated by the Application layer. Looks fantastic,
but there are some problems.

Http request data != Application layer DTO data


First, web request data does not always repeat application layer DTO. When Web
UI asks user to change password, it shows a field for old password, a field for
new password and a field to repeat new password. Web request validation should
check that “new password” and “repeat new password” fields values are the same.
Application layer doesn’t care about it. It only gets old password and new password
values and does the job. Another case: one field for some request, email for example,
will be filled by current user’s email. If this email is somehow empty, application
layer will return validation exception like “Wrong email!” and it will be shown to
the user. User will see “Wrong email” error in the form that doesn’t even have this
field! It’s definitely not an example of good UI practices.

Complex data structures


Imagine some CreateTaxiOrderDto. It’s an avia taxi, so orders can be from one
country to another. fromHouse, fromStreet, fromCity, fromState, fromCountry,
toHouse, toStreet, toCity,… Huge DTO with a lot of fields duplicating each other,
depending on each other. House fields values mean nothing without street fields
values. Street without city, state and country. And imagine the chaos when the taxi
becomes inter-galactic!
Validation 119

Validation of some fields can be dependent on others. User leaves his contact
information, email and/or phone. The business requirement is to have at least one
of them: email or phone. So, in each DTO we have to implement something like
‘required_without’ Laravel rule for both of these fields. Not very convenient.

Value objects
There is a solution right in the RegisterUserDto. We don’t store $birthDay, $birth-
Month and $birthYear there. We just store a DateTime object! We don’t validate
it each time. It always stores a correct date and time value. When we compare two
DateTime values, we don’t compare their years, months, etc. It is better to use diff()
method or comparison operators. So, all knowledge about date time value type is
consolidated in one class. Let’s try to do the same with RegisterUserDto values:

final class Email


{
/** @var string */
private $email;

private function __construct(string $email)


{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException(
'Email ' . $email . ' is not valid');
}

$this->email = $email;
}

public static function create(string $email)


{
return new static($email);
}

public function value(): string


Validation 120

{
return $this->email;
}
}

final class UserName


{
/** @var string */
private $name;

private function __construct(string $name)


{
if (/* Some validation of $name value*.
It depends on project requirements. */) {
throw new InvalidArgumentException(
'Invalid user name: ' . $name);
}

$this->name = $name;
}

public static function create(string $name)


{
return new static($name);
}

public function value(): string


{
return $this->name;
}
}

final class RegisterUserDto


{
// fields and constructor
Validation 121

public function getUserName(): UserName


{...}

public function getEmail(): Email


{...}

public function getBirthDate(): DateTime


{...}
}

Yes, creating a class for each input value is not what developers are dreaming about.
But it’s just a natural way of application decomposition. Instead of validating strings
and using strings as values, which can result in unvalidated data somewhere, these
classes only allow you to have correct, validated data. This pattern is called Value
Object(VO). Now everyone can trust in values returned by these getters. getEmail()
doesn’t return a meaningless string value. It returns a real email value, which can be
used without any fear. UserService class now can trust the values and use them:

final class UserService


{
public function register(RegisterUserDto $dto)
{
//...
$user = new User();
$user->name = $dto->getName()->value();
$user->email = $dto->getEmail()->value();
$user->birthDate = $dto->getBirthDate();

$user->saveOrFail();
}
}

Yes, ->value() calls look a bit weird. I think it can be solved by overriding __-
toString() magic method of Email and UserName class, but I’m not be sure if it
works with Eloquent values. Even if it works, it’s implicit magic. I don’t like these
kinds of solutions. Later we will try to deal with this problem.
Validation 122

VO as composition of values
Email and UserName value objects are basically just wrappers for strings. Value
objects are a much wider concept. Geo-point is a structure with two float values:
Latitude and Longitude.
Usually nobody is interested in a Latitude value without a Longitude( if the first one
is not a 90 or -90 :) ). By creating a GeoPoint(float $latitude, float $longitude) value
object, almost all program code can work with Geo coordinates as one type, without
remembering that it’s two doubles. The same thing we do with DateTime.

final class GeoPoint


{
/** @var float */
private $latitude;

/** @var float */


private $longitude;

public function __construct(float $latitude, float $longitude)


{
$this->latitude = $latitude;
$this->longitude = $longitude;
}

public function getLatitude(): float


{
return $this->latitude;
}

public function getLongitude(): float


{
return $this->longitude;
}

public function isEqual(GeoPoint $other): bool


Validation 123

{
// Just for example
return $this->getDistance($other)->getMeters() < 10;
}

public function getDistance(GeoPoint $other): Distance


{
// Calculating distance between $this and $other points
}
}

final class City


{
//...
public function setCenterPoint(GeoPoint $centerPoint)
{
$this->centerLatitude = $centerPoint->getLatitude();
$this->centerLongitude = $centerPoint->getLongitude();
}

public function getCenterPoint(): GeoPoint


{
return new GeoPoint(
$this->centerLatitude, $this->centerLongitude);
}
}

In this example, City class encapsulated latitude and longitude storing details and
provides only a GeoPoint instance. All other app code can use it to calculate distances
between points and other things without thinking about how this class works.
Other examples of value objects:

• Money(int amount, Currency currency)


• Address(string street, string city, string state, string country, string zipcode)
Validation 124

Have you noticed that in the last example I’m trying to not use primitive types,
like string, int, float? getDistance() returns a Distance object, not int or float.
Distance object has methods like getMeters(): float or getMiles(): float. Dis-
tance::isEqual(Distance $other) can be used to compare two distances. It’s a Value
Object, too! Well, sometimes it’s over-engineering. For some projects, Point::getDistance():
float returning a value in meters will be enough. I just wanted to show an example of
which I call ‘Thinking by objects’. We will return to Value Objects later in this book.
As you maybe understand, VO is too powerful a thing to use only as DTO fields.

Value object is not for user data validation


Current DTO and controller code:

final class RegisterUserDto


{
// fields and constructor

public function getUserName(): UserName


{...}

public function getEmail(): Email


{...}

public function getBirthDate(): DateTime


{...}
}

final class UserController extends Controller


{
public function register(
Request $request, UserService $service)
{
$this->validate($request, [
'name' => 'required',
'email' => 'required|email',
Validation 125

'birth_date' => 'required|date',


]);

$service->register(new RegisterUserDto(
UserName::create($request['name']),
Email::create($request['email']),
DateTime::createFromFormat('some format', $request)
));

//return ok response
}
}

It’s easy to find duplications in this code. Email value has first been validated with
Laravel validation $this->validate() and then in the Email constructor:

final class Email


{
private function __construct(string $email)
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException(
'Email ' . $email . ' is not valid');
}

$this->email = $email;
}

public static function create(string $email)


{
return new static($email);
}
}

The idea of removing this duplication looks interesting. $this->validate() call can be
removed and replaced by catching InvalidArgumentException in the global error
Validation 126

handler. This idea looks good only at first sight. As I mentioned before, “Http request
data != Application layer DTO data”. User can get validation error messages not about
his data in this case.
Value objects can be used not only for validation purposes. There are cases when an
error inside the application will be interpreted as a validation error, and this is not the
experience the user wants to have. If you remember, PhpStorm by default has 3 root
classes for unchecked exceptions: Error, RuntimeException and LogicException:

• Error represents some PHP language inner exceptions, like TypeError, ParseEr-
ror, etc.
• RuntimeException represents an error which can be thrown only in runtime
and the reason is not in our code(like database connection issues).
• InvalidArgumentException extends LogicException. LogicException de-
scription from php documentation: “Exception that represents error in the
program logic. This kind of exception should lead directly to a fix in your code.”
So, if code is written well, it should never throw LogicException. That means
the checks in VO constructors are just to be sure that the data was already
checked earlier. It’s kind of an application code verification, not user input
validation.

Conclusion
Moving business logic to the application layer results in some issues with data
validation. Web FormRequest objects can’t be used anymore, and some kind of Data
transfer objects should be used instead (it might be the usual PHP arrays or special
DTO classes). If the application layer DTO always represents user input, then the
user input validation can be moved to the application layer and implemented by
symfony/validator or another validation package. It will be a bit dangerous and
sometimes not convenient with complex data.
Validation can be left in Web, API and other parts. DTO will have data without any
additional checks. So, the application layer should just trust the data, processed by
callers. From my experience, it works only in small projects. Big projects written
by a team of developers and this approach will always lead to some incidents with
corrupted data in the database or just runtime errors.
Validation 127

The Value Object pattern requires some additional coding and “thinking by objects”
from developers, but they provide the most safe and natural data representation. As
always, it’s a trade-off between short term and long term productivity.
8. Events
Procrastinator’s rule: if something can be postponed, it should be postponed.

Application layer action often contains the main action and some secondary actions.
User registration contains user entity creation and register email sending actions.
Post text updating contains updating $post->text with saving and, for example,
Cache::forget calls for deleting old values from cache. Pseudo-real example: site with
polls. Poll creation:

final class PollService


{
/** @var BadWordsFilter */
private $badWordsFilter;

public function __construct(BadWordsFilter $badWordsFilter


/*, ... other dependencies */)
{
$this->badWordsFilter = $badWordsFilter;
}

public function create(PollCreateDto $request)


{
$poll = new Poll();
$poll->question = $this->badWordsFilter->filter(
$request->getQuestion());
//...
$poll->save();

foreach($request->getOptionTexts() as $optionText)
Events 129

{
$pollOption = new PollOption();
$pollOption->poll_id = $poll->id;
$pollOption->text =
$this->badWordsFilter->filter($optionText);
$pollOption->save();
}

// Call sitemap generator

// Notify external API about new poll


}
}

Here is a poll and options creation action with filtering for bad words in all texts
and some post-actions. Poll object is not simple. It’s absolutely useless if it has no
options. We have to take care about it’s consistency. This little period of time when
Poll object already created and saved to the database before PollOption objects were
also saved to the database and added to Poll object is very dangerous.

Database transactions
First problem - database. Some error can happen and the Poll object will be saved but
the PollOption objects not. Or at least not all of them. All modern database engines
created for storing data whose consistency is important have transaction support.
Database transactions guarantee the consistency in the database. We can run some
queries under them and all of them will be executed. If some error happens (database
error or exception in user code) all queries in the transaction will be rolled back and
have no effect on the database data. Looks like a solution:
Events 130

final class PollService


{
/** @var DatabaseConnection */
private $connection;

public function __construct(..., DatabaseConnection $connection)


{
...
$this->connection = $connection;
}

public function create(PollCreateDto $request)


{
$this->connection->transaction(function() use ($request) {
$poll = new Poll();
$poll->question = $this->badWordsFilter->filter(
$request->getQuestion());
//...
$poll->save();

foreach($request->getOptionTexts() as $optionText) {
$pollOption = new PollOption();
$pollOption->poll_id = $poll->id;
$pollOption->text =
$this->badWordsFilter->filter($optionText);
$pollOption->save();
}

// Call sitemap generator

// Notify external API about new poll


});
}
}

Okay, now our data will be consistent in the database, but this transaction magic
Events 131

is not free for database engines. When we run queries in transaction, DBMS have
to store two versions of data: for successful and fail cases. In high-load projects
there may be hundreds of concurrent transactions and when the execution time
of each transaction is long, it may drastically reduce performance. For a non-high-
load project it’s not very important, but it is still better to make it a habit to run
transactions as fast as possible. All post actions should definitely be moved outside
of the transaction. Bad words filter can be our inner service or some external API
call (which can take a lot of time). It also should be moved outside of the transaction.

final class PollService


{
public function create(PollCreateDto $request)
{
$filteredRequest = $this->filterRequest($request);

$this->connection->transaction(
function() use ($filteredRequest) {
$poll = new Poll();
$poll->question = $filteredRequest->getQuestion();
//...
$poll->save();

foreach($filteredRequest->getOptionTexts()
as $optionText) {
$pollOption = new PollOption();
$pollOption->poll_id = $poll->id;
$pollOption->text = $optionText;
$pollOption->save();
}
});

// Call sitemap generator

// Notify external API about new poll


}
Events 132

private function filterRequest(


PollCreateDto $request): PollCreateDto
{
// filters the request texts
// and return the same DTO but with filtered data
}
}

Queues
Second problem - request execution time. Application should respond as fast as
possible. Poll creation contains some heavy operations like sitemap update and
external API calling. Common solution - postpone some actions using queues.
Instead of executing some heavy action in the web request, the application can create
a task to execute this action and put it to a queue. A queue can be a database table,
Redis list or special queue solutions, like RabbitMQ. Laravel suggests several ways
to work with queues. One of them: jobs. As I said before, usually there is one main
action and some secondary actions. The main action here is creating a Poll object
and it can’t be executed without filtering texts. All post-actions can be executed
later. Actually, in some rare cases when it takes too long, the whole application layer
action can be executed in a queued job. But this is not our case.

final class SitemapGenerationJob implements ShouldQueue


{
public function handle()
{
// Call sitemap generator
}
}

final class NotifyExternalApiJob implements ShouldQueue {}

use Illuminate\Contracts\Bus\Dispatcher;
Events 133

final class PollService


{
/** @var Dispatcher */
private $dispatcher;

public function __construct(..., Dispatcher $dispatcher)


{
...
$this->dispatcher = $dispatcher;
}

public function create(PollCreateDto $request)


{
$filteredRequest = $this->filterRequest($request);

$poll = new Poll();


$this->connection->transaction(...);

$this->dispatcher->dispatch(
new SitemapGenerationJob());
$this->dispatcher->dispatch(
new NotifyExternalApiJob($poll->id));
}
}

If the Laravel job class implements empty interface ShouldQueue, it’s execution will
be queued. Well, this code now executes pretty fast, but I still don’t like it. There can
be a lot of post-actions in big projects and the service class starts to know too much.
It executes the main action and knows about all the post-actions. From a high-level
point of view, poll creation action should not know about sitemap generation. If it
hypothetically will be moved to another project, without sitemaps it couldn’t work
there. Also, in a project with a lot of actions and post-actions, developers need a
convenient configuration for which post-actions should be called after which action.
Events 134

Events
Instead of directly calling all post-actions, the service action can just inform the
application that something happened. The application then can react by executing
all needed post-actions. Laravel has an events feature for that.

final class PollCreated


{
/** @var int */
private $pollId;

public function __construct(int $pollId)


{
$this->pollId = $pollId;
}

public function getPollId(): int


{
return $this->pollId;
}
}

use Illuminate\Contracts\Events\Dispatcher;

final class PollService


{
/** @var Dispatcher */
private $dispatcher;

public function __construct(..., Dispatcher $dispatcher)


{
...
$this->dispatcher = $dispatcher;
}
Events 135

public function create(PollCreateDto $request)


{
// ...

$poll = new Poll();


$this->connection->transaction(
function() use ($filteredRequest, $poll) {
// ...
});

$this->dispatcher->dispatch(new PollCreated($poll->id));
}
}

final class SitemapGenerationListener implements ShouldQueue


{
public function handle($event)
{
// Call sitemap generator
}
}

final class EventServiceProvider extends ServiceProvider


{
protected $listen = [
PollCreated::class => [
SitemapGenerationListener::class,
NotifyExternalApiListener::class,
],
];
}

Application layer now only notifies the application that something is happened.
PollCreated or something else. The application has configuration on how to react to
these events. Listener class handles the subscribed events. ShouldQueue interface
works the same as with jobs, as a mark when it should be processed: immediately or
Events 136

queued. Events is a powerful mechanism, but it has a lot of traps.

Using Eloquent events


Laravel fires a lot of system events. Cache events: CacheHit, CacheMissed, etc.
Notification events: NotificationSent, NotificationFailed, etc. Eloquent also fires
its own events. Example from documentation:

class User extends Authenticatable


{
/**
* The event map for the model.
*
* @var array
*/
protected $dispatchesEvents = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}

UserSaved event will be fired each time the User model will be “saved” to database.
“Saved” means any insert or update SQL query done by this entity class. Using these
events has a lot of disadvantages.
UserSaved is not a good name for this event. UsersTableRowInsertedOrUpdated
is more correct. But even this is sometimes wrong. This event won’t be fired in
bulk update operations. Deleted event won’t be fired if the row will be deleted by
the database cascade delete operation. The main problem is that these events are
infrastructure events, database rows events, but they are used as business or domain
events. The difference is easy to see in our poll creation example:
Events 137

final class PollService


{
public function create(PollCreateDto $request)
{
//...
$this->connection->transaction(function() use (...) {
$poll = new Poll();
$poll->question = $filteredRequest->getQuestion();
//...
$poll->save();

foreach($filteredRequest->getOptionTexts() as $optionText){
$pollOption = new PollOption();
$pollOption->poll_id = $poll->id;
$pollOption->text = $optionText;
$pollOption->save();
}
});
//...
}
}

$poll->save(); call will fire ‘saved’ event. First problem here is that the Poll object
is still not ready and inconsistent. It doesn’t have options yet. If the event listener
wants to build, for example, an email about a new poll, it will definitely try to
fetch all options. Yes, for queued listeners it’s not a problem, but in developers’
machines the QUEUE_DRIVER value is ‘sync’, so all queued jobs/listeners become
“not queued” and execute immediately. I strongly recommend to avoid solutions
which work “sometimes, in some conditions”.
The second problem that these events will be fired inside a transaction. Running
listeners immediately or putting them to queue will make the transaction much
longer and more fragile. Even worse, that event, like PollCreated, will be fired, but
the transaction can be rolled back for some reason! Email with poll, which wasn’t
even created, will be sent to the user. I found Laravel packages that collect these
events, wait for the transaction to be committed, and only then run them (google
“Laravel transactional events”). So, they are trying to fix both of these problems, but
Events 138

it looks so unnatural! The simple idea to fire normal domain event PollCreated after
the transaction is successfully committed is much better.

Entities as event fields


The second trap is providing entity objects as an event field.

final class PollCreated


{
/** @var Poll */
private $poll;

public function __construct(Poll $poll)


{
$this->poll = $poll;
}

public function getPoll(): Poll


{
return $this->poll;
}
}

final class PollService


{
public function create(PollCreateDto $request)
{
// ...
$poll = new Poll();
// ...
$this->dispatcher->dispatch(new PollCreated($poll));
}
}

final class SendPollCreatedEmailListener implements ShouldQueue


Events 139

{
public function handle(PollCreated $event)
{
// ...
foreach($event->getPoll()->options as $option)
{...}
}
}

It’s just an example of listener, which uses some HasMany relation values. This code
works okay. When the listener will ask $event->getPoll()->options, Eloquent makes
a query to the database and fetches fresh values for this relation. Another example:

final class PollOptionAdded


{
/** @var Poll */
private $poll;

public function __construct(Poll $poll)


{
$this->poll = $poll;
}

public function getPoll(): Poll


{
return $this->poll;
}
}

final class PollService


{
public function addOption(PollAddOptionDto $request)
{
$poll = Poll::findOrFail($request->getPollId());

if($poll->options->count() >= Poll::MAX_POSSIBLE_OPTIONS) {


Events 140

throw new BusinessException('Max options amount exceeded');


}

$poll->options()->create(...);

$this->dispatcher->dispatch(new PollOptionAdded($poll));
}
}

final class SomeListener implements ShouldQueue


{
public function handle(PollOptionAdded $event)
{
// ...
foreach($event->getPoll()->options as $option)
{...}
}
}

Here is the trap. When the service class checks the options count, it gets the fresh
options of the poll. Then it adds the new option to the poll by $poll->options()-
>create(…); Then the listener asks $event->getPoll()->options and gets an old copy
of options without the newly added one. This is an Eloquent behaviour, which has
two different interfaces to relations. options() method and options pseudo-field. So,
to provide entities to events, the developer has to pay attention to the consistency of
entities. For this case:

$poll->load('options');

should be run before the event firing. The same loading should be called to each
relation which can be changed by an action. That’s why for Eloquent entities I
recommend just providing the id, not the whole entity. Listeners will always fetch a
fresh entity by id.
9. Unit testing
100% coverage should be a consequence, not the aim.

First steps
You have perhaps heard about unit testing. It’s quite popular nowadays. I often talk
with developers who don’t start to write any features without writing a unit test for
it. TDD guys. It’s very difficult to start writing unit tests for your code, especially
if you’re using a framework like Laravel. Unit tests are one the best indicators for a
project’s code quality. Frameworks like Laravel are trying to make development as
rapid as possible, and allow you to cut corners in many places. High-coupled and
low-cohesive code is the usual price for that. Entities hardwired to the database,
classes with multiple dependencies which are very hard to find(Laravel facades). In
this chapter I’ll try to test the Laravel application code and show the main difficulties,
but let’s start from the very beginning:
Pure function - the function, with a result depending only on given parameters,
with values only used for reading.
Examples:

function strpos(string $needle, string $haystack)


function array_chunk(array $input, $size, $preserve_keys = null)

Pure functions are very simple and predictable. Their unit tests are the easiest and
simplest unit tests ever.
Our first example will also be a pure function. It can be a function or a method of
the stateless class. So, starting by unit tests:
Unit testing 142

function cutString(string $source, int $limit): string


{
return ''; // some dummy code
}

class CutStringTest extends \PHPUnit\Framework\TestCase


{
public function testEmpty()
{
$this->assertEquals('', cutString('', 20));
}

public function testShortString()


{
$this->assertEquals('short', cutString('short', 20));
}

public function testCut()


{
$this->assertEquals('long string shoul...',
cutString('long string should be cut', 20));
}
}

I used PHPUnit framework to test this function. The function name isn’t the best,
but just by reading its tests, you can easily understand what exactly this function
should do. Unit tests might be good documentation for your code. If I run my tests,
the PHPUnit output will be:
Unit testing 143

Failed asserting that two strings are equal.


Expected :'short'
Actual :''

Failed asserting that two strings are equal.


Expected :'long string shoul...'
Actual :''

Obviously, our function doesn’t do it’s job. It’s time to implement:

function cutString(string $source, int $limit): string


{
$len = strlen($source);

if($len < $limit) {


return $source;
}

return substr($source, 0, $limit-3) . '...';


}

PHPUnit output after that:

OK (3 tests, 3 assertions)

Good. The unit test class contains the requirements to function:

• For an empty string, the result also should be empty.


• For strings that don’t exceed the limit, the result should be the same.
• For a string longer than the limit, the result should be cut to the needed limit
with 3 dots.

Successful tests mean that our function satisfies its requirements. But that’s not true!
I’ve made a little mistake and the function works incorrectly if the source string
length equals the limit. Good habit: if a bug was found, a test that reproduces this
bug should be created first. Anyway, we should check whether this bug was fixed or
not and a test is the best place for that check. New test methods:
Unit testing 144

class CutStringTest extends \PHPUnit\Framework\TestCase


{
// Old tests here

public function testLimit()


{
$this->assertEquals('limit', cutString('limit', 5));
}

public function testBeyondTheLimit()


{
$this->assertEquals('beyondl...',
cutString('beyondlimit', 10));
}
}

testBeyondTheLimit is ok, but testLimit fails:

Failed asserting that two strings are equal.


Expected :'limit'
Actual :'li...'

Fix is simple: just replace < by <=

function cutString(string $source, int $limit): string


{
$len = strlen($source);

if($len <= $limit) {


return $source;
}

return substr($source, 0, $limit-3) . '...';


}

Run tests immediately:


Unit testing 145

OK (5 tests, 5 assertions)

Awesome. Checking these boundary values (0, $limit length, $limit+1 length, etc.)
is an important part of testing. A lot of mistakes happen there.
When I was implementing the cutString function, I thought the source string length
would be used twice and saved it to a variable. Now I can remove it.

function cutString(string $source, int $limit): string


{
if(strlen($source) <= strlen($source)) {
return $source;
}

return substr($source, 0, $limit-3) . '...';


}

And again: run tests! I did a refactoring and could have broken something. Better
to check it as soon as possible. This feature increases productivity a lot. With good
tests, almost every mistake will be caught immediately after making it, when the
developer can fix it very quickly.
I put all my attention on the main functionality, but forgot about pre-conditions.
Obviously, $limit parameter value will never be too small in a real project, but good
function design assumes checking this value also:

function cutString(string $source, int $limit): string


{
if($limit < 5) {
throw new InvalidArgumentException(
'The limit is too low');
}

if(strlen($source) <= strlen($source)) {


return $source;
}
Unit testing 146

return substr($source, 0, $limit-3) . '...';


}

class CutStringTest extends \PHPUnit\Framework\TestCase


{
//...

public function testLimitCondition()


{
$this->expectException(InvalidArgumentException::class);

cutString('limit', 4);
}
}

Testing stateful classes


Pure functions are great, but there are a lot of things in the real world that can’t
be described exclusively with them. Objects can have a state. Unit testing of these
classes becomes more complex. There is a recommendation for the test method
structure to have 3 steps:

1. initialize the object and its state


2. run the tested action
3. check the result

I’ve also heard about the AAA pattern: Arrange, Act, Assert, which
describes the same steps.

Pure function testing also has these steps, but usually all of them are placed in one
line. Here is a simple example with an imaginary Post entity(it’s not an Eloquent
entity). It can only be created with a non-empty title, but the body can be empty. If
we want to publish this post, the body also should be non-empty.
Unit testing 147

class Post
{
/** @var string */
public $title;

/** @var string */


public $body;

/** @var bool */


public $published;

public function __construct(string $title, string $body)


{
if (empty($title)) {
throw new InvalidArgumentException(
'Title should not be empty');
}

$this->title = $title;
$this->body = $body;
$this->published = false;
}

public function publish()


{
if (empty($this->body)) {
throw new CantPublishException(
'Cant publish post with empty body');
}

$this->published = true;
}
}

The Post class constructor is a pure function, so tests for it look similar to the previous
example, and the initialize and run steps are located in one line:
Unit testing 148

class CreatePostTest extends \PHPUnit\Framework\TestCase


{
public function testSuccessfulCreate()
{
// initialize and run
$post = new Post('title', '');

// check
$this->assertEquals('title', $post->title);
}

public function testEmptyTitle()


{
// check
$this->expectException(InvalidArgumentException::class);

// initialize and run


new Post('', '');
}
}

Otherwise, the publish method depends on the post’s current state and its tests have
more transparent steps:

class PublishPostTest extends \PHPUnit\Framework\TestCase


{
public function testSuccessfulPublish()
{
// initialize
$post = new Post('title', 'body');

// run
$post->publish();

// check
$this->assertTrue($post->published);
Unit testing 149

public function testPublishEmptyBody()


{
// initialize
$post = new Post('title', '');

// check
$this->expectException(CantPublishException::class);

// run
$post->publish();
}
}

In case of testing exceptions, the check step, which is usually the last one, should be
before the run step. Testing stateful classes is a bit more complex than testing pure
functions, and a developer who writes a test should keep the entity’s state in mind.

Testing classes with dependencies


One of the most important characteristics of unit testing is testing a unit in isolation.
The unit should be isolated from the rest of the world. It guarantees that the test
will test only this unit. The test can only fail for 2 reasons: wrong test or wrong unit
code. Isolation gives us simplicity and performance. True unit tests run very quickly
because they usually don’t make heavy I/O operations like database queries or API
calls. When a class depends on some other functionality, the test should somehow
provide it.

Real class dependencies


In the DI chapter, I was talking about two cases with interfaces:

1. there is an interface and more than one implementations.


Unit testing 150

2. there is an interface and only one implementation.

For the second case, I suggested to not create an interface. I want to analyze
this case now. What dependency can be implemented in only one way? All I/O
related dependencies, such as API calls, file operations, and database queries always
have other possible implementations (with another driver, decorator pattern, etc.).
Sometimes a class contains some big computations and a developer decides to move
it to its own class. This new class becomes a dependency for the old one. In that case,
another implementation for this class is hard to imagine. This is a great time to talk
about encapsulation and why unit testing is called “unit testing” (not “class testing”).
Here is an example of the described case. TaxCalculator was moved to its own class
from OrderService.

class OrderService
{
/** @var TaxCalculator $taxCalculator */
private $taxCalculator;

public function __construct(TaxCalculator $taxCalculator)


{
$this->taxCalculator = $taxCalculator;
}

public function create(OrderCreateDto $orderCreateDto)


{
$order = new Order();
//...
$order->sum = ...;
$order->taxSum = $this->taxCalculator
->calculateOrderTax($order);
//...
}
}

But if we look at this OrderService class, we can see that TaxCalculator doesn’t
Unit testing 151

look like its dependency. It doesn’t look like something from the outside world that
OrderService needs to work. It looks like a part of the OrderService class.

OrderService here is a unit, which contains not only the OrderService class, but
also a TaxCalculator class. TaxCalculator should be an inner dependency, not outer.

class OrderService
{
/** @var TaxCalculator $taxCalculator */
private $taxCalculator;

public function __construct()


{
$this->taxCalculator = new TaxCalculator();
}

//...
}

Unit tests can now test the OrderService class without providing the TaxCalculator
dependency. In case of requirement changes when TaxCalculator becomes an
outer dependency (some parameters needed for calculation could be moved to the
database), it can easily be used as a public dependency, provided as a constructor
parameter. Only the constructor code and some tests will be changed.
Unit is a very wide concept. In the beginning of this chapter, we were testing a
unit which contained only one little function, cutString. In some cases, units can
contain several classes. Programming objects inside a unit should be focused on
one responsibility, in other words, have strong cohesion. This is what the Single
Responsibility Principle from SOLID is all about.
Unit testing 152

When the methods of a class are totally independent from each other, the class is not
a unit. Each method in the class is a unit by itself. If you’re going to unit test this
class, it may be better to extract the methods to their own classes (do you remember
that I prefer *Command classes for each application layer action instead of *Service
classes?). It can simplify the unit testing.

Stubs and fakes


Usual public dependency has an interface and some implementation(s) that are used
in the real app. Using real implementations during unit testing is not a good idea.
Usually they do some I/O operations or have dependencies which do it. It is usually
unacceptable and also makes unit tests too slow. Unit tests should be fast because
they will be executed very often. Slow unit test execution can decrease developers’
productivity a lot. The most natural solution is to create a dummy implementation
of the needed interface and provide it to the class on the unit test. Returning to the
previous example, imagine that a requirement really changed and TaxCalculator
now has an interface with some implementation.

interface TaxCalculator
{
public function calculateOrderTax(Order $order): float;
}

class OrderService
{
/** @var TaxCalculator $taxCalculator */
private $taxCalculator;

public function __construct(TaxCalculator $taxCalculator)


{
$this->taxCalculator = $taxCalculator;
}

public function create(OrderCreateDto $orderCreateDto)


{
Unit testing 153

$order = new Order();


//...
$order->sum = ...;
$order->taxSum = $this->taxCalculator
->calculateOrderTax($order);
//...
}
}

class FakeTaxCalculator implements TaxCalculator


{
public function calculateOrderTax(Order $order): float
{
return 0;
}
}

class OrderServiceTest extends \PHPUnit\Framework\TestCase


{
public function testCreate()
{
$orderService = new OrderService(new FakeTaxCalculator());

$orderService->create(new OrderCreateDto(...));

// some assertions
}
}

Works! These classes are called fakes. Unit test libraries can create dummy imple-
mentations on the fly. The same test using PhpUnit’s createMock method:
Unit testing 154

class OrderServiceTest extends \PHPUnit\Framework\TestCase


{
public function testCreate()
{
$stub = $this->createMock(TaxCalculator::class);

$stub->method('calculateOrderTax')
->willReturn(0);

$orderService = new OrderService($stub);

$orderService->create(new OrderCreateDto(...));

// some assertions
}
}

They are convenient if you need a simple implementation once. If there are a lot of
usages of TaxCalculator, the fake is the more preferable solution. Mock libraries can
create stubs not only for interfaces, but for real classes too, which can be useful for
legacy projects or for projects with lazy developers. :)

Mocks
Sometimes a developer wants to test whether this stub method called and which
parameters were provided there.

Actually, it may not be the best idea to check each dependency’s method
call. In that case, a unit test knows a lot about how this unit works. As
a consequence, these unit tests usually become too easy to break. A little
refactoring and the unit test fails. If it happens too often, the team can just
forget about unit testing. It’s called “white box” testing. The “black box”
testing style tries only test inputs and outputs of the unit without trying to
check how it works inside. Obviously, black box tests are more stable.

This checking can be implemented in the fake class, but it is not trivial and you
Unit testing 155

definitely don’t want to do it for each dependency. Mocking libraries provide this
functionality.

class OrderServiceTest extends \PHPUnit\Framework\TestCase


{
public function testCreate()
{
$stub = $this->createMock(TaxCalculator::class);

// Configure the stub.


$stub->expects($this->once())
->method('calculateOrderTax')
->willReturn(0);

$orderService = new OrderService($stub);

$orderService->create(new OrderCreateDto(...));

// some assertions
}
}

Now the test checks that during the OrderService::create method execution, Tax-
Calculator::calculateOrderTax was called exactly once. Mock methods have a lot
of features for parameter value checking, returning values configuring, throwing
exceptions, etc. I don’t want to focus on it in this book. Fakes, stubs and mocks have
a common name - test doubles, which means objects which stand in for real objects
in test. They can be used not only in unit tests but in integration tests also.

Software testing types


People invented a ton of possible types of software testing. Security testing to find
possible vulnerabilities. Performance testing to check application performance. In
this chapter, we will focus on application correctness testing. We already took a look
Unit testing 156

at unit testing, where units were tested in isolation. Integration testing tests several
units working together.
Example: ask UserService to register a new user and check that a new row was saved
to the database, the needed event(UserRegistered) was generated and an email for
this user was sent(at least the framework was asked to send it).

Functional testing is about checking the whole app to accept its functional require-
ments.
Example: Requirement about creating some entity (this process can be described in
detail in QA’s documents). Test opens the browser, goes to the specified page, fills
inputs, presses the Create button and checks that the needed entity was created by
finding it on another page.

Laravel testing
Laravel (current version is 5.8) provides some tools to simplify various kinds of
testing.
Unit testing 157

Functional testing
Tools for HTTP testing, browser testing and console testing make functional testing
in Laravel very convenient, but as usual I don’t like the examples in documentation.
One of them, but a little bit changed:

class ExampleTest extends TestCase


{
public function testBasicExample()
{
$response = $this->postJson('/users', [
'name' => 'Sally',
'email' => 'sally@example.com'
]);

$response
->assertOk()
->assertJson([
'created' => true,
]);
}
}

This test just tests that POST /user request returns a successful result. It looks incom-
plete. The test should verify that the entity was really created. How? First answer:
just make a query for the database and check it. Example from documentation again:
Unit testing 158

class ExampleTest extends TestCase


{
public function testDatabase()
{
// Make call to application...

$this->assertDatabaseHas('users', [
'email' => 'sally@example.com'
]);
}
}

Good. Let’s write another test the same way:

class PostsTest extends TestCase


{
public function testDelete()
{
$response = $this->deleteJson('/posts/1');

$response->assertOk();

$this->assertDatabaseMissing('posts', [
'id' => 1
]);
}
}

And here is a little trap. The same trap as we met in the Validation chapter. Just by
adding the SoftDeletes trait to the Post class, this test will be broken. Application
requirements haven’t changed, application from the user’s point of view works
absolutely the same. Functional tests should not be broken in that case. Tests which
make a request to the application and go to the database to check the result of actions
aren’t true functional tests. They know too much about how the application works,
how it stores the data, and which table and fields are used. It is an another example
of white box testing.
Unit testing 159

As I already mentioned, functional testing is about checking the whole app to accept
it’s functional requirements. Functional requirements are not about the database,
they are about the whole application. True functional tests should work only outside
the app.

class PostsTest extends TestCase


{
/**
* A basic test example.
*
* @return void
*/
public function testCreate()
{
$response = $this->postJson('/api/posts', [
'title' => 'Post test title'
]);

$response
->assertOk()
->assertJsonStructure([
'id',
]);

$checkResponse = $this->getJson(
'/api/posts/' . $response->getData()->id);

$checkResponse
->assertOk()
->assertJson([
'title' => 'Post test title',
]);
}

public function testDelete()


{
Unit testing 160

// Some initialization here to create a post with id=$postId


// Make sure that post exists in application
$this->getJson('/api/posts/' . $postId)
->assertOk();

// Delete post command


$this->jsonDelete('/posts/1')
->assertOk();

// Check that post doesn't exist in application


$this->getJson('/api/posts/' . $postId)
->assertStatus(404);
}
}

At delete test, we don’t care if it is soft deleted, or deleted by ‘delete’ sql query.
Functional test just checks that the application behaves as expected. Expected
behavior for deleted entity - it isn’t shown anywhere in the app and the test should
check it.
Schema of data flow for “create post” and “get post” request processing:
Unit testing 161

What functional tests should see:


Unit testing 162

Facades mocking
Laravel also suggests to use their convenient implementation of Service Locator
pattern - Laravel facades. I always say “Laravel facade”, because there is a Facade
structural design pattern and people can misunderstand which facade is used. In one
project, two developers said that I don’t understand how Laravel facades work and I
had to write comments like this:

/**
* It is NOT a Laravel facade.
* It's an implementation of a Facade pattern from GoF.
*/
final class SomeFacade
{
//...
}

Laravel not only suggests to use them but also provides good tools for testing the code,
which uses facades. Let’s try to write one of the previous examples with facades and
test it:

class Poll extends Model


{
public function options()
{
return $this->hasMany(PollOption::class);
}
}

class PollOption extends Model


{
}

class PollCreated
{
Unit testing 163

/** @var int */


private $pollId;

public function __construct(int $pollId)


{
$this->pollId = $pollId;
}

public function getPollId(): int


{
return $this->pollId;
}
}

class PollCreateDto
{
/** @var string */
public $title;

/** @var string[] */


public $options;

public function __construct(string $title, array $options)


{
$this->title = $title;
$this->options = $options;
}
}

class PollService
{
public function create(PollCreateDto $dto)
{
if(count($dto->options) < 2) {
throw new BusinessException(
"Please provide at least 2 options");
Unit testing 164

$poll = new Poll();

\DB::transaction(function() use ($dto, $poll) {


$poll->title = $dto->title;
$poll->save();

foreach ($dto->options as $option) {


$poll->options()->create([
'text' => $option,
]);
}
});

\Event::dispatch(new PollCreated($poll->id));
}
}

class PollServiceTest extends TestCase


{
public function testCreate()
{
\Event::fake();

$postService = new PollService();


$postService->create(new PollCreateDto(
'test title',
['option1', 'option2']));

\Event::assertDispatched(PollCreated::class);
}
}

• Event::fake() call transforms Event facade to mock object.


Unit testing 165

• PostService::create creates a poll with options, saves it to the database and


dispatches a PollCreated event.
• Event::assertDispatched checks that the needed event was dispatched.

Disadvantages I see here:

• It’s not a unit test. The Event facade was mocked, but database was not. Real
table rows are inserted during test execution. To make this test more clear, the
RefreshDatabase trait is usually used to re-create the database for each test. It’s
very slow. One test can be executed this way in a reasonable time, but hundreds
of tests will be executed over several minutes. It’s not acceptable.
• The database is touched during this test. That means the database in an ideal
case should be the same as in production. Sometimes it’s not possible and issues
happen locally, but not in production or visa versa, which can make these tests
almost useless. Also, some developers store their projects in virtual machines,
but want to run tests under a host machine, which doesn’t have the database
and other environment installed.
• Test checks only event dispatching. To check database operations, the devel-
oper has to use methods like assertDatabaseHas or something like PollSer-
vice::getById, which makes the test look like a functional test for the application
layer.
• PollService class dependencies aren’t described explicitly. To check what it
needs to work, we have to scan the whole source. It makes PollService test
writing more complicated. Even worse, if some new dependency will be added
to this class, tests will continue working using a real implementation of this
dependency: real API calls, real file creating, etc.

I call this “forced integration testing”. Developer wants to create a unit test, but the
code is so high coupled, so hardwired with framework (in this example with Service
locator and Eloquent)… I’m going to try to decouple the code from the framework
in the next section.
Unit testing 166

Application layer unit testing

Decouple from Laravel facades


To test the PollService::create method in isolation, I have to decouple it from Laravel
facades and the database(Eloquent). First task is simple, as Laravel facades are easy
to change by DI.

• Event facade represents IlluminateContractsEventsDispatcher interface.


• DB facade - IlluminateDatabaseConnectionInterface.

Well, the last one isn’t correct. DB facade represents IlluminateDatabaseDatabase-


Manager which has this “magic”:

class DatabaseManager
{
/**
* Dynamically pass methods to the default connection.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return $this->connection()->$method(...$parameters);
}
}

As you see, Laravel uses PHP magic a lot and sometimes doesn’t respect OOP
principles. Thanks to barryvdh/laravel-ide-helper package, which helps to find the
real implementors of some Laravel facade methods.
Unit testing 167

class PollService
{
/** @var \Illuminate\Database\ConnectionInterface */
private $connection;

/** @var \Illuminate\Contracts\Events\Dispatcher */


private $dispatcher;

public function __construct(


ConnectionInterface $connection, Dispatcher $dispatcher)
{
$this->connection = $connection;
$this->dispatcher = $dispatcher;
}

public function create(PollCreateDto $dto)


{
if(count($dto->options) < 2) {
throw new BusinessException(
"Please provide at least 2 options");
}

$poll = new Poll();

$this->connection->transaction(function() use ($dto, $poll) {


$poll->title = $dto->title;
$poll->save();

foreach ($dto->options as $option) {


$poll->options()->create([
'text' => $option,
]);
}
});

$this->dispatcher->dispatch(new PollCreated($poll->id));
Unit testing 168

}
}

Good. For the ConnectionInterface test double I can create a FakeConnection class.
Laravel’s EventFake class, which was used to replace Event after Event::fake() call,
can be used independently.

use Illuminate\Support\Testing\Fakes\EventFake;
//...

class PollServiceTest extends TestCase


{
public function testCreatePoll()
{
$eventFake = new EventFake(
$this->createMock(Dispatcher::class));

$postService = new PollService(


new FakeConnection(), $eventFake);

$postService->create(new PollCreateDto(
'test title',
['option1', 'option2']));

$eventFake->assertDispatched(PollCreated::class);
}
}

The test looks almost the same as with facades, but now it is more strict for the
PollService class. Each added dependency has to be added to the tests, also. Does it?
Actually, if some other developer adds a Laravel facade using the PollService class,
nothing happens and the test will work as usual, even calling this facade! It happens
because by default, Laravel provides its own base TestCase class, which sets up the
Laravel environment.
Unit testing 169

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase


{
use CreatesApplication;
}

That’s why I usually change it to a pure PhpUnit TestCase class:

abstract class TestCase extends \PHPUnit\Framework\TestCase


{
}

Now if someone adds SomeFacade facade call, the test will fail:

Error : Class 'SomeFacade' not found

Well, I decoupled PollService from Laravel facades.

Decouple from database


Decoupling from the database is more complex. Let’s create a repository class to use
instead of Eloquent actions that touch the database.

interface PollRepository
{
//... some other actions

public function save(Poll $poll);

public function saveOption(PollOption $pollOption);


}

class EloquentPollRepository implements PollRepository


{
Unit testing 170

//... some other actions

public function save(Poll $poll)


{
$poll->save();
}

public function saveOption(PollOption $pollOption)


{
$pollOption->save();
}
}

class PollService
{
/** @var \Illuminate\Database\ConnectionInterface */
private $connection;

/** @var PollRepository */


private $repository;

/** @var \Illuminate\Contracts\Events\Dispatcher */


private $dispatcher;

public function __construct(


ConnectionInterface $connection,
PollRepository $repository,
Dispatcher $dispatcher)
{
$this->connection = $connection;
$this->repository = $repository;
$this->dispatcher = $dispatcher;
}

public function create(PollCreateDto $dto)


{
Unit testing 171

if(count($dto->options) < 2) {
throw new BusinessException(
"Please provide at least 2 options");
}

$poll = new Poll();

$this->connection->transaction(function() use ($dto, $poll) {


$poll->title = $dto->title;
$this->repository->save($poll);

foreach ($dto->options as $optionText) {


$pollOption = new PollOption();
$pollOption->poll_id = $poll->id;
$pollOption->text = $optionText;

$this->repository->saveOption($pollOption);
}
});

$this->dispatcher->dispatch(new PollCreated($poll->id));
}
}

class PollServiceTest extends \PHPUnit\Framework\TestCase


{
public function testCreatePoll()
{
$eventFake = new EventFake(
$this->createMock(Dispatcher::class));

$repositoryMock = $this->createMock(PollRepository::class);

$repositoryMock->method('save')
->with($this->callback(function(Poll $poll) {
return $poll->title == 'test title';
Unit testing 172

}));

$repositoryMock->expects($this->at(2))
->method('saveOption');

$postService = new PollService(


new FakeConnection(), $repositoryMock, $eventFake);

$postService->create(new PollCreateDto(
'test title',
['option1', 'option2']));

$eventFake->assertDispatched(PollCreated::class);
}
}

This is a correct unit test. PollService was tested in isolation, without Laravel
environment. But why am I not very happy about this? The reasons are:

• I had to create a Repository abstraction just to make my code testable.


PollService code without it looks more readable, which is important. It looks
like a Repository pattern, but it’s not. It just substitutes all Eloquent database
operations. If the Poll entity will have more relations, the repository class
should have a save%RelationName% method for each of them.
• Almost all Eloquent methods are restricted. Yes, they will work correctly in real
request processing, but not in unit tests. Over time, developers will ask “do we
really need these unit tests?”
• On the other hand, unit tests are very complicated. This example only checks
that the poll with two options was correctly created. Even for this simple
example I had to use two fakes and one mock.
• Each new dependency addition or removal breaks the tests. It makes unit test
supporting harder.

It’s hard to measure, but I think the benefits of these unit tests are less than the time
spent to write/support them and reduces the readability of the application layer. In
Unit testing 173

the beginning of this chapter I said that “unit tests are one the best indicators for
projects code quality”. If the code is hard to test, it means the code is low cohesive
and/or high coupled. PollService is low cohesive. It violates the Single Responsibility
Principle. It contains the core logic (checking options count and creating poll with
options) and the application logic (database transactions, events dispatching, bad
words filter in previous edition, etc.). This can be solved by separating Domain and
Application layers. The next chapter is about that.

Application testing strategy


In this section I don’t want to speak about big companies, which usually create the
testing strategy right after starting the new project. I want to talk about little projects
that begin to grow. First, the project was tested just team members using it. Over
time, the project owner, manager or developer opens the app, does some actions and
checks the correctness or how beautiful the UI is. It’s a non-automated Functional
testing without any strategy.
Then in the same stage (usually after some painful bugs in the production), the team
understands they need to change something.
First, the obvious solution is to hire a manual software tester. He can describe the
main use cases for the app and after each app update go through these use cases and
check that the application meets the requirements (it’s called regression testing).
Then, if the application continues growing, the number of use cases also grows. At
the same time, the team wants to make updates more often and it becomes totally
impossible to manually test the application for each update. The solution is to write
automated functional tests. Use cases written in the first stage can be converted to
test scenarios for Selenium or other tools for functional testing.
Functional testing is the most important testing from a client’s/product owner’s point
of view.
What about unit tests? Yes, they can help to check very special cases that are hard
to check in functional testing, but the main benefit from them - they should help
us to write the code. Do you remember the example with the cutString function in
the beginning of this chapter? It was much easier to write the code, which meets
its requirements with unit tests. Writing unit tests should be easy. They shouldn’t
Unit testing 174

be a heavy burden of the project, which is not very stable and always takes time to
support. If you have some pure functions or some stateful classes, unit tests can help
you to create them with the needed quality quicker than doing it without unit tests.
The application layer unit tests are too difficult to create and support. “Forced
integration” tests are easier to create but might be very unstable. If your project has
the core logic, which you definitely want to check how it will behave in boundary
cases, it may be a big hint for you. Maybe the project core logic is already big and
complex enough to separate it from Application layer to its own Domain layer.
10. Domain layer
private $name, getName() and setName($name) is NOT an encapsulation!

When and why?


Complex web applications can be classified into two classes:

• Complex application logic and average (or low) core logic. Example: content
projects. Application logic has SEO actions, integrations with different API’s,
authorization, searching the content. Core logic is very simple in comparison
with application logic.
• Complex core logic and complex or not very complex application logic. Exam-
ple: games, enterprise apps.
Domain layer 176

It’s easy to predict that I suggest to extract core logic (which is usually called Domain
or Business logic) to its own layer for a second class of complex applications (for other
apps it also might be useful, but it’s not easy to prove and depends on many factors).
This moving to its own layer might be very difficult, especially for Laravel projects,
because Eloquent is hardwired with database, so creating a pure domain layer with
Eloquent entities is impossible. I’ll try to discuss the reasons for creating a Domain
layer. Each of them is not a super strong argument, but together they can prove the
need for extracting a Domain layer for some apps.

Unit testing
In the previous chapter I’ve found that unit testing application logic might be very
difficult. So for applications with not very large Domain logic, nobody usually wants
to unit test the application layer. Functional tests are much more important for this
kind of app.
On the other hand, complex logic is hard to test with functional tests. Functional tests
are very expensive. It takes some time to write and, more important, it takes a lot of
time to run, because it needs all application environments installed and involved for
Domain layer 177

the test run. Thousands of unit tests can be run in several seconds. Thousands of
functional tests could run for hours.
Using unit testing for complex Domain logic can increase team productivity, but as
we saw in the previous chapter, testing Domain logic inside the Application layer is
definitely not the good idea. Each unit test should provide test doubles for application
layer dependencies, just to check the business logic.
Implementing the business logic in a separated layer makes the unit testing much
easier.

Easier code maintenance


Implementing two different types of logic (application and business) in the same
unit violates the Single Responsibility Principle. Punishment comes quickly. This
code easily becomes hard to support. The number of copy-pasted code blocks grows
because refactoring is difficult. Each try to extract logic encounters resistance: two
different logics intertwined, and extracting something requires pre-separating them.
Extracting business logic to its own layer allows the developer to concentrate similar
logic in one place, which always makes refactoring and maintenance of this section
of code easier.

Active record and data mapper


Eloquent is an implementation of the Active Record pattern. Eloquent entity rep-
resents the database table row and is inseparable from the database. Each time I
write code of an Eloquent entity class, I keep in mind that $this->posts is not just a
collection of posts. It’s a database relation, a set of related table rows. But I can’t just
add a new post there, I should do something like $this->posts()->create(…);
This code can’t be unit tested. I also can’t write without thinking about the database.
The cognitive load for writing the code becomes higher and higher when the business
logic grows.
Data Mapper libraries allow to write business entities without thinking about the
database. Entities there are usual classes and they can be used even without the
database. Unit testing them is very easy. Unit tests just work with natural entities,
Domain layer 178

which only implement the business logic. When a developer wants to store this
entity in the database, it asks Data Mapper library to persist entity and the library
does this using some meta-information about how each class and field should be
persisted in database. I saw two Data Mapper ORM libraries working in Laravel
projects: Doctrine and Analogue. I’ll use the first one for examples. The following
“reasons” will describe advantages of using Data Mapper entities instead of Active
record entities.

High cohesion for Domain logic


Let’s return to an example with poll entity. Poll is very simple entity that contains
question text and possible answers (options). Obvious condition: there should be
always at least two options. As you see, “create poll” action checks that before cre-
ating poll object. This condition also makes the PollOption entity not independent!
Application can’t just delete a poll option. “delete poll option” action should check
how many options the poll has. In case of only two options, the option can’t be deleted
because poll can’t be left with only one option. So, knowledge about the minimum of
two options for a poll is implemented in two application layer actions: “create poll”
and “delete poll option”. Cohesion of this knowledge is low! It’s implemented in two
methods of usually different classes.
It happened because Poll and PollOption are not independent entities. It is one unit
- Poll, which contains options. This knowledge about the minimal number of options
should be implemented in one place, in the Poll entity.
Well, this simple example can’t prove the importance of working with entities as
a unit when they are real units. Let me imagine something more complex - an
implementation of a Monopoly game. Everything in this game is one big unit.
Players, their properties, their money, their position on the board. All of this
represents the current state of the game. A player makes the move and the next
state completely depends on the current state. If he steps on someone’s property, he
should pay. If he has enough money, he pays. If not, he should earn the money or
retreat. If the property is free, he can buy it. If he doesn’t have enough money, an
auction between other players begins.
Monopoly is a good example of a complex unit. Its implementation in different
application layer actions will contain a lot of duplicated logic. Creating a Monopoly
Domain layer 179

game unit in the Domain layer allows the developer to concentrate the game logic
in one place and check a lot of possible cases by unit testing it.

Move focus from database to domain.


The app, especially when complex, is not a mediator from UI and database. Single
player games played in a local computer don’t persist its own state after each action.
Game objects just live in memory, without any persisting. Only if the user asks to
save the game, then it persists the state to the file. This is how an application should
work. Web applications have to persist the state after each request, not because it’s
more convenient. It’s a necessary evil. In an “ideal” world with applications, which
are written once and never updated, working 100% stable in a 100% stable server with
unlimited memory, the best solution is to keep everything in application memory,
without persisting anything to databases. This idea looks crazy, especially for PHP
developers (php script dies after each request), but it makes sense. From this point
of view, it is much better to have usual objects that contain the business logic and
have no idea about such strange things - database tables. This will help developers
to focus on core logic, not on infrastructure issues.
Example project: freelance market. Some freelance market apps have different
registration/login parts for clients (users who post jobs) and freelancers. Other apps
have common registration and allow users to act as a client and as a freelancer. First
ones usually have two different tables, like clients and freelancers. Second ones only
have a users table.
In a project with Eloquent-like libraries, developers have to choose entities depending
on database structure. For Job actions: Client & Freelancer or User & User. But for
core logic and the language we use to describe it, it should not depend on something
not in the domain. When I describe job posting and applying processes, I want
to use entities with natural names. With names that are understandable not only
for developers, but also for anybody who knows the domain. I mean Client and
Freelancer.
I also want to use meaningful Value objects in the domain. If a client has an email, I
want to use Email object, not a string. If some entity has an address value, I want to
work with Address object, not with five string values.
Domain layer 180

Data Mapper libraries usually have support for flexibly mapping entities and value
objects to corresponding tables and fields.

Move focus from UI to domain


Some developers start their projects from creating a migration to database. Others
start a projects from building a user interface (UI). All these factors affect our code.
Once I helped to create a web application for a developers’ conference. Just a website
with a schedule, making orders and payment. In the beginning we had an easy admin
panel with CRUD-like forms for orders. Simple form with customer name, order
cost, order status (request, payed, cancel) and some details. When we decided to
track all attendees in our app, the process became a bit complicated. Conference
partners who wanted to participate had to create an order, tell the order number to the
organizer, who changes the order cost to zero and status to… payed? No. We had to
separate these members from others, so a new order status appeared - “partner”. Then
“speaker”, “press”, etc. Another status, “guaranteed”, appeared for cases when some
business couldn’t pay on time, but guaranteed to do it later. The UI form continued
to be very “simple” but the cognitive load for working with it was growing.

It happens very often. The customer, product owner or developer doesn’t want to
change the old UI, even if requirements have changed a lot. Application code tries to
satisfy the UI requirements, which become incorrect. Incorrect UI results in incorrect
domain, the code’s cognitive load also grows, and the project easily becomes hard
to support. For the conference, we decided to separate special members (speakers,
Domain layer 181

partners, press) and orders. Order status returned to three values: request, cancel
and accepted. New field acceptReason explains why this order is accepted: payed,
guaranteed, cash (when customer wants to pay by cash on conference day). Yes, we
created some new entities, but several simple classes are much better than one very
complex class. The same rule goes for UI. When we changed the UI according to
our new domain, the organizers weren’t very happy, but a bit later (1-2 days) they
understood that it became much more logical and didn’t allow for mistakes.

A well-designed domain allows a developer to build a good UI and database structure.


Unit testing can show the flaws of poor-designed domain more quickly than UI/UX
analysis.

Entities classes invariant


Class invariant is a condition for an object’s state which should always be fulfilled.
Each method that changes the object’s state (including a constructor) should preserve
invariant. Examples: client should always have an email, poll should always have
at least two options. The requirement of preserving an invariant after each write-
method call forces implementing invariant checks in each of them.
I didn’t find a good name for changing state methods, so let me call them “write-
methods”. “Read-method” just returns some info from a class. It’s the same as
setter/getters, but works not only for one value.
By the way, the idea of immutable objects makes this task easy. The invariant should
Domain layer 182

only be checked in the constructor. There are no other write-methods. All value
objects should be immutable and as you see, the Email object only checks the email
in the constructor. In case of the setEmail method, the check should be duplicated
there. So, more easy fulfillment of invariant is one of the advantages of immutable
objects.
It’s very hard to preserve the invariant in Eloquent entities. Eloquent doesn’t allow
to control the constructor. PollOption objects are not under Poll object control. Each
part of the application code can just call remove() method of PollOption and it will
be removed. Yes, this argument can be easily rejected by advice to not do such a
stupid thing like removing options without any checks, but the real domain can be
much more complicated and the team can be very large and contain developers with
different levels and understanding of domain. If code doesn’t allow a developer to
make the mistake, obviously it’s more stable than code which relies only on smart
and high-level developers.

Implementation of Domain layer

Which logic is “business”


Once, I’d found a set of Laravel “best” practices, there were very strange pieces of
advice, and one of them was:
“Business logic should be in the service class” with example:

public function store(Request $request)


{
$this->articleService
->handleUploadedImage($request->file('image'));

....
}

class ArticleService
{
public function handleUploadedImage($image)
Domain layer 183

{
if (!is_null($image)) {
$image->move(public_path('images') . 'temp');
}
}
}

This illustrates how difficult it could be to understand what business logic is. The
“business” is the keyword. Commercial development is not for fun. It creates a
software that solves business tasks. An e-commerce app works with products, orders,
discounts, etc. A blog app works with posts and other content. Images are also
content, but for the majority of apps, images are uploaded to local or cloud storage
and only the path of it is provided to the main entity (like a blog post). The image
itself can be a part of business logic only for a graphical editor or OCR apps.
The image uploading process is not a business logic, it’s an infrastructure logic.
Infrastructure code allows the domain layer to live inside the application and satisfy
the functional requirements. It contains working with databases, file storage, external
APIs, queue engines, etc. When domain logic is extracted from application layer, the
last one will contain only an orchestration between infrastructure and domain.

Domain example
Let’s create some domain logic. Freelance market is a good choice.

final class Client


{
/** @var Email */
private $email;

private function __construct(Email $email)


{
$this->email = $email;
}

public static function register(Email $email): Client


Domain layer 184

{
return new Client($email);
}
}

Just a client entity.


Where is the client’s first and last names?
I don’t need it for now. Who knows, the app might be with anonymous clients. If a
project owner asks to add names, they will be added.
Why do I need email?
I have to identify clients somehow.
Where is setEmail and getEmail methods?
I’ll add them as soon as I need them.

final class Freelancer


{
/** @var Email */
private $email;

/** @var Money */


private $hourRate;

private function __construct(Email $email, Money $hourRate)


{
$this->email = $email;
$this->hourRate = $hourRate;
}

public static function register(


Email $email, Money $hourRate): Freelancer
{
return new Freelancer($email, $hourRate);
}
}
Domain layer 185

A Freelancer entity. Hour rate was added comparing the Customer. Money is the
value object used for money representation in domain. What fields will it store?
Integer or float with string for currency? Freelancer entity doesn’t care about this.
Money just represents money. It can do everything the domain needs from it -
comparing with another money object, some mathematical calculations.
In the beginning, a project works with one currency and stores money information
in one integer field (amount of cents). Later, the project can support other currencies.
Some fields will be added to database, and the Money class will be changed.
Other core logic will stay unchanged because it shouldn’t be involved in this
change. This is an example of Information Hiding principle. The Money class
provides a stable interface for working with money concept. getAmount():int and
getCurrency():string methods aren’t good candidates for stable interface. Money
class users (I mean the code which uses the class) will know too much about the
inner structure and each change of it will result in massive changes in project.
equalTo(Money $other) and compare(Money $other) methods hide all unneeded
information from callers. Information hiding makes interfaces more stable. Less
interface change - less pain during code support.
Next, client can post a job. Job has some details, like title, description, estimated
budget, but job logic doesn’t depend on these values. Freelancer’s applying logic
will work even without job title. User interface can be changed in the future, some
job image can be added. Remembering the Information hiding principle, I hide
information of job details in its own value object.

final class JobDescription


{
// value object.
// Job title, description, estimated budget, etc.
}

final class Job


{
/** @var Client */
private $client;

/** @var JobDescription */


Domain layer 186

private $description;

private function __construct(Client $client,


JobDescription $description)
{
$this->client = $client;
$this->description = $description;
}

public static function post(Customer $client,


JobDescription $description): Job
{
return new Job($client, $description);
}
}

Okay, the basic entities structure is built. Let’s add some logic. Freelancer can apply
for a job. Freelancer’s proposal for job contains cover letter and his current hour rate.
He can change his hour rate, but proposal’s hour rate should not be changed in that
case.

final class Proposal


{
/**
* @var Job
*/
private $job;

/**
* @var Freelancer
*/
private $freelancer;

/**
* @var Money
*/
Domain layer 187

private $hourRate;

/**
* @var string
*/
private $coverLetter;

public function __construct(Job $job,


Freelancer $freelancer, Money $hourRate, string $coverLetter)
{
$this->job = $job;
$this->freelancer = $freelancer;
$this->hourRate = $hourRate;
$this->coverLetter = $coverLetter;
}
}

final class Job


{
//...

/**
* @var Proposal[]
*/
private $proposals;

protected function __construct(


Client $client, JobDescription $description)
{
$this->client = $client;
$this->description = $description;
$this->proposals = [];
}

public function addProposal(Freelancer $freelancer,


Money $hourRate, string $coverLetter)
Domain layer 188

{
$this->proposals[] = new Proposal($this,
$freelancer, $hourRate, $coverLetter);
}
}

final class Freelancer


{
//...

public function apply(Job $job, string $coverLetter)


{
$job->addProposal($this, $this->hourRate, $coverLetter);
}
}

Here is another example of Information Hiding. Only the Freelancer entity knows
about his hour rate. It asks job to add a new proposal with its own hour rate. Each
object has minimum necessary information to work. The system built this way is very
stable. Each requirement change usually requires changes in 1-2 classes, because the
inner structure of any class is hidden.
If something is changed there, but the class interface isn’t, there is no need to change
other classes. This code is less prone to bugs.
Freelancer can’t add another proposal to the same job. He should change the old
one instead. Later, in database level, this condition can be duplicated by a unique
index for job_id and freelancer_id fields in the proposals table, but it should be
implemented in the domain layer, also. Let’s try.
Domain layer 189

final class Proposal


{
// ..

public function getFreelancer(): Freelancer


{
return $this->freelancer;
}
}

final class Freelancer


{
public function equals(Freelancer $other): bool
{
return $this->email->equals($other->email);
}
}

final class Job


{
//...

public function addProposal(Freelancer $freelancer,


Money $hourRate, string $coverLetter)
{
$newProposal = new Proposal($this,
$freelancer, $hourRate, $coverLetter);

foreach($this->proposals as $proposal) {
if($proposal->getFreelancer()
->equals($newProposal->getFreelancer())) {
throw new BusinessException(
'This freelancer already made a proposal');
}
}
Domain layer 190

$this->proposals[] = $newProposal;
}
}

I added equals() method to Freelancer entity. Unique emails is a usual condition for
systems, so if two freelancers’ entities have the same emails, it’s the same freelancer.
Class Job is beginning to know too much about proposals. If you look inside this
foreach structure, there are only Proposal class getter calls and working with their
results. Martin Fowler named this problem “Feature Envy”. The solution is obvious
- move this part of code to the Proposal class.

final class Proposal


{
//...

/**
* @param Proposal $other
* @throws BusinessException
*/
public function checkCompatibility(Proposal $other)
{
if($this->freelancer->equals($other->freelancer)) {
throw new BusinessException(
'This freelancer already made a proposal');
}
}
}

final class Job


{
/**
* ...
* @throws BusinessException
*/
public function addProposal(Freelancer $freelancer,
Money $hourRate, string $coverLetter)
Domain layer 191

{
$newProposal = new Proposal($this,
$freelancer, $hourRate, $coverLetter);

foreach($this->proposals as $proposal) {
$proposal->checkCompatibility($newProposal);
}

$this->proposals[] = $newProposal;
}
}

Proposal::getFreelancer() method is not used anymore and can be deleted.


Encapsulation is one of the basic principles of object oriented programming, but I
often see a wrong interpretation of it. “public $name is not an encapsulation, but
private $name and getName and setName methods is”. I don’t see the difference
between public field and getter+setter methods. They both show the inner structure
of this class to client code. It allows the use of values of this class’ fields in other
classes. Class becomes a simple storage for some values, not a real owner of them.
It’s okay for some classes, like DTO, but not for business entities. Concentrating
all logic that works with class fields in this class makes the code highly cohesive.
Changes in this logic will touch only this class, not several classes in different parts
of the application. Encapsulation, being part of Information Hiding, is also a good
goal to aim for.
I already wrote some logic but totally forgot about unit testing!
Domain layer 192

class ClientTest extends TestCase


{
public function testRegister()
{
$client = Client::register(Email::create('some@email.com'));

// Assert what?
}
}

Client entity doesn’t have any getter methods. Unit test can’t check anything.
PHPUnit will show an error: “This test did not perform any assertions”. I can
implement a getEmail() method and check at least the email, but this method will
be executed only in unit tests. It won’t be participating in any business operations. I
don’t want to add the code, which will be executed only in tests, to the domain…

Domain events
It’s a good time to remember about domain events! They won’t be used only for tests.
The application can send an email for the client, after registration. Client can fire the
ClientRegistered event, but here are two problems.
When all business logic was in the Application layer, the action just used a dispatcher
object. Domain entity doesn’t have any dispatcher objects. The most popular solution
is to store generated events in an array inside the entity and clear them each time
they will be asked outside.
Domain layer 193

final class Client


{
//...

private $events = [];

public function releaseEvents(): array


{
$events = $this->events;
$this->events = [];

return $events;
}

protected function record($event)


{
$this->events[] = $event;
}
}

Entity can record events by record method. releaseEvents method returns all
previously recorded events and clears the events array. It guarantees that each event
won’t be dispatched twice.
What should the ClientRegistered event contain? I said that I want to use email to
identify clients, so ClientRegistered event can contain a client’s email but it’s not a
good idea. In the real world, email is not a good identification. Clients can change
their emails. Also it is not very effective to use it as a key in the database.
The most popular solution for entity identification is an integer field with auto-
incremental value implemented in database. It’s simple, convenient, but looks logical
only when the domain layer isn’t extracted from other code. One of the advantages of
using pure domain entities is consistency, but in the period of time between creating
and persisting the entity to the database, it will be inconsistent (id value is empty).
Previously, I mentioned that some heavy requests’ executions might be moved to
queue. How will the client know where to get a result in that case? Application should
generate an id, put the task to the queue and return this id to the client. Client will
Domain layer 194

use this id to get the result later.


I still didn’t say that I need an id to unit test the entities creation, but I don’t like the
“unit testing” argument. Inconvenient unit testing can only help to find the problems
in the code. If something needs to be added or changed in the code just for unit tests,
something is wrong with this code or tests.
Code with providing the id to entities and recording the events:

final class Client


{
private $id;

/**
* @var Email
*/
private $email;

protected function __construct($id, Email $email)


{
$this->id = $id;
$this->email = $email;
}

public static function register($id, Email $email): Client


{
$client = new Client($id, $email);
$client->record(new ClientRegistered($client->id));

return $client;
}
}

The ClientRegistered event is recorded in named constructor, not in default, because


it has the business name “register” and knows that the client really registered. Some
clients importing command might be added in the future and the event will be
another one.
Domain layer 195

final class Client


{
public static function importFromCsv($id, Email $email): Client
{
$client = new Client($id, $email);
$client->record(new ClientImportedFromCsv($client->id));

return $client;
}
}

Id generation
Our code now requires the id provided outside, but how to generate them? The
database auto-incremental column does this job flawlessly and it will be hard to
replace it. Continuing to use the auto-incremental value in Redis/Memcache is not a
good idea, because it adds a new single point of failure to the system.
The most popular non-auto-incremental algorithm is Universally unique identifier
- UUID. It’s a 128-bit value, which can be generated by several standard algo-
rithms(RFC 4122) with a probability of duplicated values close to zero. A lot of
projects use UUIDs as identifiers for their entities. I also heard that some projects
use UUIDs as identifiers, but usual auto-incremental columns as a primary key.
There is a package for working with UUIDs in PHP - ramsey/uuid. It implements
some algorithms of RFC 4122 standard. Now we can write our tests:
Domain layer 196

final class Client


{
public static function register(
UuidInterface $id,
Email $email): Client
{
$client = new Client($id, $email);
$client->record(new ClientRegistered($client->id));

return $client;
}
}

trait CreationTrait
{
private function createUuid(): UuidInterface
{
return Uuid::uuid4();
}

private function createEmail(): Email


{
static $i = 0;

return Email::create("test$i@test.test");
}
}

class ClientTest extends UnitTestCase


{
use CreationTrait;

public function testRegister()


{
$client = Client::register($this->createUuid(),
$this->createEmail());
Domain layer 197

$this->assertEventsHas(ClientRegistered::class,
$client->releaseEvents());
}
}

I’ve implemented a simple assertion (assertEventsHas) in the base UnitTestCase


class which checks that an event of the specified class exists in a given array. Do
you remember black box and white box testing? Here is an example of black box
testing. Test doesn’t get an email property with getEmail() getter or another way and
compare it with the email that was used in creation. It just checks that the needed
event was recorded.
Another test case:

class JobApplyTest extends UnitTestCase


{
use CreationTrait;

public function testApply()


{
$job = $this->createJob();
$freelancer = $this->createFreelancer();

$freelancer->apply($job, 'cover letter');

$this->assertEventsHas(FreelancerAppliedForJob::class,
$freelancer->releaseEvents());
}

public function testApplySameFreelancer()


{
$job = $this->createJob();
$freelancer = $this->createFreelancer();

$freelancer->apply($job, 'cover letter');


Domain layer 198

$this->expectException(
SameFreelancerProposalException::class);

$freelancer->apply($job, 'another cover letter');


}

private function createJob(): Job


{
return Job::post(
$this->createUuid(),
$this->createClient(),
JobDescription::create('Simple job', 'Do nothing'));
}
}

These tests are just describing our core logic. Simple applying for a job. The case of
same freelancer applying to job again. Tests check pure domain logic. There are no
mocks, stubs, database working, etc. There is no trying to get the proposals array and
checking something there. You can ask anyone who is not a developer but knows a
domain and he will understand the logic of these tests. And these tests are definitely
easy to write and they can be written together with logic.
Well, this domain is not complex enough to show the advantages of extracting it
and building with convenient unit testing, but keep in mind the Monopoly game.
Complex logic is much simpler to implement and support when it is covered by unit
tests and extracted from any infrastructure (database, HTTP, etc.).
Creation of a good domain model is not a trivial task. I can recommend two
books: Classical “Domain-Driven Design: Tackling Complexity in the Heart of
Software” by Eric Evans and “Implementing Domain-Driven Design” by Vaughn
Vernon. There are a lot of new concepts with examples from practice which can
change the way you build a domain model: Aggregate root, Bounded context,
Ubiquitous language… After reading these great books, you will maybe understand
that my current model isn’t ideal, but it suits nicely for demo purposes. I am just
trying to show the possibility of building a pure domain model in PHP.
Domain layer 199

Mapping model to database


Now, after building some logic, it’s time to map our entities and value objects to
database tables and fields. I’ll use Doctrine library for that. laravel-doctrine/orm
package provides a convenient integration of it to Laravel projects. Doctrine allows
us to use different ways to configure mapping. Example with PHP array:

return [
'App\Article' => [
'type' => 'entity',
'table' => 'articles',
'id' => [
'id' => [
'type' => 'integer',
'generator' => [
'strategy' => 'auto'
]
],
],
'fields' => [
'title' => [
'type' => 'string'
]
]
]
];

Some developers prefer to leave entities without any information about database
mapping and this outside configuration is a good option for them. But most
developers just use annotations - tags added to phpDoc. Java has native support
for annotations, PHP doesn’t, but Doctrine analyzes them and uses them to make
mapping much more convenient. Example with annotation mapping:
Domain layer 200

use Doctrine\ORM\Mapping AS ORM;

/** @ORM\Embeddable */
final class Email
{
/**
* @var string
* @ORM\Column(type="string")
*/
private $email;
//...
}

/** @ORM\Embeddable */
final class Money
{
/**
* @var int
* @ORM\Column(type="integer")
*/
private $amount;
// ...
}

/** @ORM\Entity */
final class Freelancer
{
/**
* @var Email
* @ORM\Embedded(class = "Email", columnPrefix = false)
*/
private $email;

/**
* @var Money
* @ORM\Embedded(class = "Money")
Domain layer 201

*/
private $hourRate;
}

/**
* @ORM\Entity()
*/
final class Job
{
/**
* @var Client
* @ORM\ManyToOne(targetEntity="Client")
* @ORM\JoinColumn(nullable=false)
*/
private $client;

//...
}

I use Embeddable annotation for my value objects and Embedded to use them
in other classes. Each annotation has parameters. Embedded requires the class
parameter to know what to embed, and optional columnPrefix for the field name
generation.
There are also annotations for different relation types: one to many, many to many,
etc.

/** @ORM\Entity */
final class Freelancer
{
/**
* @var UuidInterface
* @ORM\Id
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="NONE")
*/
Domain layer 202

private $id;
}

/** @ORM\Entity */
final class Proposal
{
/**
* @var int
* @ORM\Id
* @ORM\Column(type="integer", unique=true)
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
}

Each entity should have an Id field. UUID fields have type “uuid”, which means they
will be stored in char(36) columns. UUID values have standard string representations.
Example: e4eaaaf2-d142-11e1-b3e4-080027620cdd Alternatively they can be stored as
16-byte binary values (check ramsey/uuid-doctrine package).
Proposal entities are the part of Job unit and they will never be created outside.
It doesn’t need an id value, but Doctrine asks to have it, so we can use an auto-
incremental column and forget about it.
I don’t want to copy and paste Doctrine documentation to this book. There are some
problems during mapping domain to database with Doctrine (for example, it still
doesn’t support nullable embeddables), but in most cases it’s possible to implement
it.
When Doctrine fetches an entity from database, it creates an object of the needed
class without using a constructor and just fills needed fields with values without
using any setter methods. It uses PHP Reflection magic for that. As a result: objects
don’t feel the database. Their lifecycle is natural.
Domain layer 203

$freelancer = new Freelancer($id, $email);

$freelancer->apply($job, 'some letter');

$freelancer->changeEmail($anotherEmail);

$freelancer->apply($anotherJob, 'another letter');

There is a lot of database fetching and at least one persisting between each method
call, which can be executed in different servers, but the object doesn’t feel them.
It just lives as it lives in some unit test case. Doctrine tries to make all needed
infrastructure work to allow the objects to live naturally.

Migrations
It’s time to create our database. Laravel migrations is a possible way to do it, but
Doctrine suggests some magic here: doctrine migrations. After installing the laravel-
doctrine/migrations package and running “php artisan doctrine:migrations:diff”,
fresh migration will appear in database/migrations folder:

class Version20190111125641 extends AbstractMigration


{
public function up(Schema $schema)
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() != '\
sqlite',
'Migration can only be executed safely on \'sqlite\'.');

$this->addSql('CREATE TABLE clients (id CHAR(36) NOT NULL --(DC2Type:uu\


id)
, email VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE TABLE freelancers (id CHAR(36) NOT NULL --(DC2Typ\
e:uuid)
, email VARCHAR(255) NOT NULL, hourRate_amount INTEGER NOT NULL,
PRIMARY KEY(id))');
Domain layer 204

$this->addSql('CREATE TABLE jobs (id CHAR(36) NOT NULL --(DC2Type:uuid)


, client_id CHAR(36) NOT NULL --(DC2Type:uuid)
, title VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL,
PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_A8936DC519EB6921 ON jobs (client_id)');
$this->addSql('CREATE TABLE proposals (id INTEGER PRIMARY KEY
AUTOINCREMENT NOT NULL
, job_id CHAR(36) DEFAULT NULL --(DC2Type:uuid)
, freelancer_id CHAR(36) NOT NULL --(DC2Type:uuid)
, cover_letter VARCHAR(255) NOT NULL, hourRate_amount INTEGER NOT NULL)\
');
$this->addSql('CREATE INDEX IDX_A5BA3A8FBE04EA9 ON proposals (job_id)');
$this->addSql('CREATE INDEX IDX_A5BA3A8F8545BDF5 ON proposals (freelanc\
er_id)');
}

public function down(Schema $schema)


{
$this->abortIf($this->connection->getDatabasePlatform()->getName() != '\
sqlite',
'Migration can only be executed safely on \'sqlite\'.');

$this->addSql('DROP TABLE clients');


$this->addSql('DROP TABLE freelancers');
$this->addSql('DROP TABLE jobs');
$this->addSql('DROP TABLE proposals');
}
}

I used sqlite for this test project. Well, they look ugly compared with clean Laravel
migrations, but Doctrine can auto-generate them! “doctrine:migrations:diff” com-
mand analyzes current database and entities’ metadata and generates a migration,
which should change the database structure for domain needs. For an empty database
it will be the creation of all needed tables. For a case when some field is added to
some entity, it will be just adding this field to the needed table.
I think that’s enough about Doctrine. It definitely allows a developer to build pure
Domain layer 205

domain and effectively map it to the database. As I said before, after extracting
Domain layer, Application layer only makes an orchestration between infrastructure
and domain.

final class FreelancersService


{
/** @var ObjectManager */
private $objectManager;

/** @var MultiDispatcher */


private $dispatcher;

public function __construct(


ObjectManager $objectManager, MultiDispatcher $dispatcher)
{
$this->objectManager = $objectManager;
$this->dispatcher = $dispatcher;
}

/**
* Return freelancers's id.
*
* @param \App\Domain\ValueObjects\Email $email
* @param \App\Domain\ValueObjects\Money $hourRate
* @return UuidInterface
*/
public function register(
Email $email, Money $hourRate): UuidInterface
{
$freelancer = Freelancer::register(
Uuid::uuid4(), $email, $hourRate);

$this->objectManager->persist($freelancer);
$this->objectManager->flush();

$this->dispatcher->multiDispatch(
Domain layer 206

$freelancer->releaseEvents());

return $freelancer->getId();
}
//...
}

Here, UUID is generated in the Application layer. It can be generated a bit earlier. I
heard about some projects which ask clients to generate UUIDs.

POST /api/freelancers/register
{
"uuid": "e4eaaaf2-d142-11e1-b3e4-080027620cdd",
"email": "some@email.com"
}

I think this way is canonical. Client asks the app to make an action and provides all
needed data. Simple “200 OK” response is enough for the client. It already has an id
and can continue to work.
Doctrine’s ObjectManager::persist method puts the entity to persisting queue. Ob-
jectManager::flush method persists everything in persisting queue to database. Lets
check a non-creation action:

interface StrictObjectManager extends ObjectManager


{
/**
* @param string $entityName
* @param $id
* @return null|object
*/
public function findOrFail(string $entityName, $id);
}

final class DoctrineStrictObjectManager


extends EntityManagerDecorator
Domain layer 207

implements StrictObjectManager
{
/**
* @param string $entityName
* @param $id
* @return null|object
*/
public function findOrFail(string $entityName, $id)
{
$entity = $this->wrapped->find($entityName, $id);

if($entity === null)


{
throw new ServiceException(...);
}

return $entity;
}
}

Here I extended the standard ObjectManager with findOrFail method, which does
the same thing as the findOrFail method in Eloquent entities. The difference is only
in the exception object. Eloquent generates an EntityNotFound exception, which
transforms to a 404 http error. My StrictObjectManager will be used only in write
operations and if some entity could not be found, it’s more like a validation error (do
you remember Laravel’s “exists” validation rule?), not an error which should return
a 404 response.
Domain layer 208

abstract class JsonRequest extends FormRequest


{
public function authorize()
{
// Don't check authorization in request classes
return true;
}

/**
* Get data to be validated from the request.
*
* @return array
*/
protected function validationData()
{
return $this->json()->all();
}
}

final class JobApplyDto


{
/** @var UuidInterface */
private $jobId;

/** @var UuidInterface */


private $freelancerId;

/** @var string */


private $coverLetter;

public function __construct(UuidInterface $jobId,


UuidInterface $freelancerId, string $coverLetter)
{
$this->jobId = $jobId;
$this->freelancerId = $freelancerId;
$this->coverLetter = $coverLetter;
Domain layer 209

public function getJobId(): UuidInterface


{
return $this->jobId;
}

public function getFreelancerId(): UuidInterface


{
return $this->freelancerId;
}

public function getCoverLetter(): string


{
return $this->coverLetter;
}
}

final class JobApplyRequest extends JsonRequest


{
public function rules()
{
return [
'jobId' => 'required|uuid',
'freelancerId' => 'required|uuid',
//'coverLetter' => optional
];
}

public function getDto(): JobApplyDto


{
return new JobApplyDto(
Uuid::fromString($this['jobId']),
Uuid::fromString($this['freelancerId']),
$this->get('coverLetter', '')
);
Domain layer 210

}
}

JsonRequest is the base class for API requests, where data is provided as JSON in
the request body. JobApplyDto is a simple DTO for a job apply action. JobApplyRe-
quest is a JsonRequest, which makes needed validation and creates a JobApplyDto
object.

final class FreelancersController extends Controller


{
/** @var FreelancersService */
private $service;

public function __construct(FreelancersService $service)


{
$this->service = $service;
}

public function apply(JobApplyRequest $request)


{
$this->service->apply($request->getDto());

return ['ok' => 1];


}
}

Controllers become very simple. They just provide data from request objects to
service action.
Domain layer 211

final class FreelancersService


{
public function apply(JobApplyDto $dto)
{
/** @var Freelancer $freelancer */
$freelancer = $this->objectManager
->findOrFail(Freelancer::class, $dto->getFreelancerId());

/** @var Job $job */


$job = $this->objectManager
->findOrFail(Job::class, $dto->getJobId());

$freelancer->apply($job, $dto->getCoverLetter());

$this->dispatcher->multiDispatch(
$freelancer->releaseEvents());

$this->objectManager->flush();
}
}

Application layer action is also simple. It asks the object manager to fetch needed
entities and asks them to make a business action. The main difference from Eloquent
is the flush method call. Application layer doesn’t have to try to save each entity
to database. Doctrine remembers all entities that were fetched and determines all
changes that were made there. Here, in flush method, it will find that a new
object was added to the proposals property of job entity and insert this row to the
database! This magic allows us to not think about how to store everything in the
database. Domain entities just add new entities to its own properties, or just changes
something. Doctrine persists all changes, even in deep relations. Application layer
just calls ObjectManager::flush method.
Of course, everything has a price. Doctrine code is much more complicated than
Eloquent’s. While working with Eloquent, I can always go to its sources and
understand why it behaves like that. I can’t say the same about Doctrine :) Its
configuration and working with complex select queries is harder than working with
Eloquent.
Domain layer 212

You can check the full source of this super basic project in my Github:
https://github.com/adelf/freelance-example
I’ve added some CQRS pattern implementations there, so it is better to read the next
chapter before.

Error handling in domain layer


In the “Error handling” chapter I suggested using an unchecked BusinessException
to reduce @throws tags amount, but for complex domains it’s not a good idea.
Exceptions can be thrown deep in the domain and at some level domain can react
to them. Even in our simple example, Freelancer asks Job to add proposal, Job asks
own proposals to check compatibility and an exception is thrown there.
Proposal object throws an exception (‘This freelancer already made a proposal’) but
it doesn’t know a context. In the context of adding a new proposal, this exception
goes to high levels (controller, global event handler) and is just shown to the user.
In another context, caller wants to know what exactly was wrong. In a complex
domain, it’s important to know what can happen in each method call, so using
checked exceptions is a good option.

// BusinessException becomes "checked"


abstract class BusinessException
extends \Exception {...}

final class SameFreelancerProposalException


extends BusinessException
{
public function __construct()
{
parent::__construct(
'This freelancer already made a proposal');
}
}
Domain layer 213

final class Proposal


{
//...

/**
* @param Proposal $other
* @throws SameFreelancerProposalException
*/
public function checkCompatibility(Proposal $other)
{
if($this->freelancer->equals($other->freelancer)) {
throw new SameFreelancerProposalException();
}
}
}

final class Job


{
//...

/**
* @param Proposal $newProposal
* @throws SameFreelancerProposalException
*/
public function addProposal(Proposal $newProposal)
{
foreach($this->proposals as $proposal)
{
$proposal->checkCompatibility($newProposal);
}

$this->proposals[] = $newProposal;
}
}

Another condition can be added with new requirements: make some kind of auction
Domain layer 214

and don’t allow user to add proposal with an hour rate higher than the existing one
(not very smart requirement, but anyway). New exception will be added to @throws
doc block. It will result in a cascade of changes in all callers. They also should add
an additional @throws doc block.
Another problem: sometimes caller needs to know all possible problems. “same
freelancer” and “too high hour rate” issues can happen at the same time, but only one
exception will be thrown. Creating a new exception to store all the reasons why this
proposal is not compatible with others and make the checkCompatibility method
code much more complicated is not a good option.
That’s why I often hear that developers prefer something like the FunctionResult
objects from the “Error handling” chapter for their domain. There is no problem with
returning an object with all issues from the checkCompatibility method. Caller’s code
becomes a bit more noisy. It’s a trade-off again.

Conclusion
Extracting a Domain layer is a big step in project evolution. It’s much more optimal to
make it in the beginning, but the architect should estimate the complexity of domain.
If a domain is just a simple CRUD with very little additional logic, there is no sense
in extracting a Domain layer. It may cost a lot of time and money without significant
benefits.
On the other hand, for complex domains it’s definitely a correct choice. Writing
pure domain logic with effortless unit testing is a big advantage. It is not easy to
switch from anemic models (when Eloquent entities store only data and logic is
implemented outside) to rich models (where data and logic are implemented in the
same classes). It’s the same as switching from procedural programming to object
oriented. It requires some time and some practice, but benefits for some projects are
also big.
As you see, getter methods (getEmail, getHourRate) are not needed to implement
write operations (apply for a job) according to Information Hiding principle. If some
getter of class A is used in class B logic, class B begins to know too much about
class A inner structure. Each class A change might result in class B (and all other
getter callers) changes. It easily becomes hard to support systems where using other
Domain layer 215

class getters is a usual practice. Each change there becomes very dangerous. Popular
developers’ joke for these systems: “It works? Please, don’t touch it!”
Sadly, systems also have a user interface where all this inner data should be shown.
Getters have to be implemented because people want to see emails and hour rates.
When entities have getters, it becomes so easy to violate Information Hiding and
other good principles, so easy to make a mistake, especially in projects with many
developers. Is there a way to not implement getters in our domain? Let’s talk about
it in the next chapter!
11. CQRS
Reading and writing - different
responsibilities?
As we mentioned in the previous chapter, getters are not needed for a correct domain
model. There are only entities and “write” methods, but the user interface has to show
information to users. Is the getter method for entities the only solution, or there are
some others? Let’s try to invent something!

Database stored procedures and views


The first project of my professional career was a huge application with logic in stored
procedures and database views. There were thousands of them. I was writing a client
for it in C++. Database view is just a saved select query which can be used later as a
read-only table (some DBMS allow to write there, but it’s a bad practice).

CREATE TABLE t (qty INT, price INT);


INSERT INTO t VALUES(3, 50);
CREATE VIEW v AS SELECT qty, price, qty*price AS value FROM t;
SELECT * FROM v;
+------+-------+-------+
| qty | price | value |
+------+-------+-------+
| 3 | 50 | 150 |
+------+-------+-------+

View example from MySQL documentation contains a new ‘value’ field which
doesn’t exist in the table.
Stored procedure is just a set of instructions written as a special procedural extension
of SQL(PL/SQL in Oracle, Transact-SQL for MSSQL and others). It’s like a PHP
function, which is executed inside the database engine.
CQRS 217

PROCEDURE raise_salary (
emp_id NUMBER,
amount NUMBER
) IS
BEGIN
UPDATE employees
SET salary = salary + amount
WHERE employee_id = emp_id;
END;

Example of very simple procedure in PL/SQL.


As I mentioned, the system was huge, there was a ton of logic. Without some
restrictions, this kind of system easily becomes totally unsupportable and non-stable.
There was some structure of views and stored procedures for each entity:

Tables were like the private fields of a class, inaccessible to select, update and other
operations. While writing this book, I realized that these stored procedures and views
implemented an Application layer for this system. I’m talking about this example
because here it is very easy to see how different write and read operations are. They
CQRS 218

use absolutely different types of database objects - procedures and views.

Master-slave replication
Another case: databases in projects where loading becomes high. First thing de-
velopers can do to reduce loading to a database is to leave one database only for
write operations and one or several databases for read operations. It’s a “master-
slave replication”.

All changes go to the master (write) database and then replicate to slave (read)
replicas. The same pattern: write requests to database go to one place, read requests
to another.
Sometimes the replication process goes slow and read replicas contain old data. Users
can change some data in the app but continue to see the old version of it. The same
can happen when data caching was implemented not very carefully. An architecture
of systems with one database and cache looks very similar to systems with write and
read databases. Cache is a kind of read storage.
These slow replications and cached old values issues will be auto-fixed after some
time. Read replicas will catch up current data. Cached values will expire. Anyway,
if the user changed something, he will see the result, sometimes not immediately,
but he will. This type of consistency is called “eventual”. Eventual consistency is a
common attribute for systems with different stores for write and read data.
CQRS 219

Developers should keep in mind eventual consistency by implementing Application


service. All queries should use a write database connection during processing of
a write operation. Not cached values, not fetched from read stores. This condition
forces the segregation of Application layer code which works with write operations
from code working with read operations.

But it’s not the only reason.

Typical Application layer class


CQRS 220

final class PostService


{
public function getById($id): Post{}
public function getLatestPosts(): array{}
public function getAuthorPosts($authorId): array{}

public function create(PostCreateDto $dto){}


public function publish($postId){}
public function delete($postId){}
}

Set of write methods with set of read methods. Manipulations with this class might
be not convenient.
Let’s try to implement data caching for read methods. If we implement caching inside
PostService read methods, these methods will have at least two responsibilities: data
fetching and caching. The most common solution is to use the Decorator pattern as
I did in the “Dependency Injection” chapter. I have to extract an interface or abstract
class from PostService class, but I have a problem with naming! interface Post-
Service, but how to name the class, implementing it? Or maybe “class PostService
implements PostServiceInterface”?
Another issue: decorator class for caching will cache read methods, but not touch
write ones. This makes us realize that the PostService class has two responsibilities:
write and read operations.
Let’s leave write methods in PostService class and make something new for read:

final class PostService


{
public function create(PostCreateDto $dto){}
public function publish($postId){}
public function delete($postId){}
}

interface PostQueries
{
CQRS 221

public function getById($id): Post;


public function getLatestPosts(): array;
public function getAuthorPosts($authorId): array;
}

final class DatabasePostQueries implements PostQueries{}

final class CachedPostQueries implements PostQueries


{
/** @var PostQueries */
private $baseQueries;

/** @var Cache */


private $cache;

public function __construct(PostQueries $baseQueries,


Cache $cache)
{
$this->baseQueries = $baseQueries;
$this->cache = $cache;
}

public function getById($id): Post


{
return $this->cache->remember('post_' . $id,
function() use($id) {
return $this->baseQueries->getById($id);
});
}
//...
}

Looks much better! Segregating write service class and read queries makes refactor-
ing and other manipulations much easier.
CQRS 222

Report queries
Report queries easily show the different natures of read and write models. Complex
queries with ‘group by’, ‘union’ and aggregation functions… When developers try to
use Eloquent entities for those kinds of queries, it always looks ugly. Entities built
for write operations are not intended to be used as report data.
After some time, an obvious solution: to use Structured Query Language(SQL)
comes to mind. Using raw SQL is much easier and natural for report queries. Data
fetched from these queries are stored in simple classes (like DTO) or just in php
arrays. This is a simple example of using a totally different model (service and
“entity” classes) for the same data.

Laravel API resources


Laravel 5.5 version introduced API resources. They can be used to transform the
data of the base Eloquent entity. It’s an attempt to solve the problem of different
data needed in read queries and the several responsibilities of the Eloquent entity
class. But they are not true read models, they still use an Eloquent entity to fetch
the data. Creating fully independent read models will remove the read responsibility
from the Eloquent entity.

Command Query Responsibility Segregation


Command Query Responsibility Segregation(CQRS) pattern suggests to have fully
independent read and write models. Model here is a set of services working with
database, Application layer, entities, value objects, etc.
Read and write models can be built with different ways and technologies. Write
model with Doctrine or custom data mapper and read model with Active Record
ORM or just raw SQL queries. Technologies and architecture for each model are
chosen according project needs.
For the application from the previous chapter with a write model implemented using
Doctrine, a read model can be implemented simply by Eloquent:
CQRS 223

namespace App\ReadModels;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

abstract class ReadModel extends Model


{
public $incrementing = false;

protected function performInsert(Builder $query)


{
throw new WriteOperationIsNotAllowedForReadModel();
}

protected function performUpdate(Builder $query)


{
throw new WriteOperationIsNotAllowedForReadModel();
}

protected function performDeleteOnModel()


{
throw new WriteOperationIsNotAllowedForReadModel();
}

public function truncate()


{
throw new WriteOperationIsNotAllowedForReadModel();
}
}

final class WriteOperationIsNotAllowedForReadModel


extends \RuntimeException
{
public function __construct()
{
parent::__construct(
CQRS 224

"Write operation is not allowed for read model");


}
}

A base class for Eloquent read models. It just overrides all write operations and blocks
them.

final class Client extends ReadModel{}

final class Freelancer extends ReadModel{}

final class Proposal extends ReadModel{}

final class Job extends ReadModel


{
public function proposals()
{
return $this->hasMany(Proposal::class, 'job_id', 'id');
}
}

final class ClientsController extends Controller


{
public function get(UuidInterface $id)
{
return Client::findOrFail($id);
}
}

final class FreelancersController extends Controller


{
public function get(UuidInterface $id)
{
return Freelancer::findOrFail($id);
}
}
CQRS 225

final class JobsController extends Controller


{
public function get(UuidInterface $id)
{
return Job::findOrFail($id);
}

public function getWithProposals(UuidInterface $id)


{
return Job::with('proposals')->findOrFail($id);
}
}

Very basic implementation. Just entities called from controllers. As you see, a read
model can be trivial, even for a complex write model with layered architecture. It can
be refactored by introducing the Queries** or **Repository classes, with caching
decorators and other improvements. The big advantage is that a read model can make
these improvements, but the write model won’t be touched!
The case with a complex read and an average write domain is also possible. Once
I had been participating in a high-load content project. Write model wasn’t very
complex, so we just extracted the Application layer and used Eloquent entities there.
But the read model contained a lot of different complicated queries and the cache
was actively used. So, for the read model we took simple read model entities, which
were usual classes and contained only public fields, like DTO.

Conclusion
As each pattern, CQRS has advantages and disadvantages. It allows to build read and
write parts of an application independently, which can help to reduce the complexity
of the write (remove getters and other functionality from write entities) or read (use
plain objects and raw SQL queries) part. On the other hand, in most cases it’s a
duplication of entities, some Application layer part, etc. It will take a lot more time
to build two models instead of only one.
CQRS 226

Read and write models often need some synchronization. Tasks for adding some new
fields to entities contain at least two sub-tasks for read and write models. Application
has to be covered well by functional tests.
12. Event sourcing
1. e4 - e5
2. Nf3 - Nc6
3. Bb5 - a6

Have you played chess? Even if not, you know this game. Two players just move the
pieces. And it is the best example of pattern that I want to talk about.

The kings game


When chess lovers want to know about some chess grandmasters game, the final
position on the board is not very interesting. They want to know about each move!

1. d4 - Nf6
2. Bg5

“What a joke from World champion!!!”


The meaning of the current position on the board fully depends on moves done
before:

1. The most important: who is moving next? The position can be switched from
winning to losing based on this.
2. Castling is possible only if the king and rook haven’t moved before.
3. En passant capture is possible only right after a target pawn double-step move.

Let’s create a chess playing web application. How we will store the games? I see two
options:
Event sourcing 228

1. store current position on the board, but with some additional information: who
moves next, castling possibilities and some info about the last move for en
passant capture. Moves can be stored in another “table”, just for history.
2. store all moves and get current position on the board by “replaying” all these
moves each time.

There are two main notations for computer chess:


FEN - stores current position with all needed additional information Example:

rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2

PGN - stores all moves Example:

[Event "F/S Return Match"]


[Site "Belgrade, Serbia JUG"]
[Date "1992.11.04"]
[Round "29"]
[White "Fischer, Robert J."]
[Black "Spassky, Boris V."]
[Result "1/2-1/2"]

1. e4 e5 2. Nf3 Nc6 3. Bb5 a6


...
42. g4 Bd3 43. Re6 1/2-1/2

As you see, both ideas are okay, but which one to choose?
First way is traditional for web applications: we always store only the current state
in database tables. Sometimes, we also have some history tables and store the logs
there: how our data was changed.
Second way looks weird. Getting the current position by replaying each move is too
slow, but having full information about the game might be very useful.
Let’s imagine two applications and analyze both options. First app stores chess games
in table: id, current position, long/short castling possibility for whites/blacks, en
passant field(if exists)
Event sourcing 229

Second app just stores all moves for each game.


Application requirements change over time, here I purposely forgot some chess rules
about draw. Fifty-move rule: the game can be declared a draw if no capture has been
made and no pawn has been moved in the last fifty moves. This rule was added to
remove the possibility of infinite games.
How our two systems will react to this change?
First system has to add a new field to table: amount of moves made without capturing
and pawn movement(FEN chess notation also has this value). This new field value
will be calculated after each move. The problem is with current playing games. The
history of it is stored in some other table, but it doesn’t participate in game logic.
Second system just implements this 50-move logic and it fluently will work, even for
current playing games without changing anything on storage.
Looks like the second system is more prepared for changes. The second rule about
draws: threefold repetition. A draw is declared if the same position occurs three
times.
I don’t know how the first app’s developers will implement this change. It needs to
have information about all previous positions. Second application developers again
just implement this logic, without any changes in storage.
As you see, in chess domain is very important to have all historical information
and not just as some logs, but as an active domain logic participant. But what if
the second application becomes very popular and a lot of people watch chess games
there? Calculating the current position for each watcher is not very effective due to
performance issues. Obvious solution from previous chapter: CQRS. A write model
system needs all moves, but a read model only needs a current position. After each
move written to the write model, the system can calculate the current position on
the board and write it to some table for read requests.
First system stored the current position on the board as a main source of information
and all moves in the history table for some analytics needs. The second system, on
the contrary, stores all moves as a main source and the current position for a read
model. As a result, the second system is more prepared for changes in core logic.
I chose chess because it is the best example of true Event Sourcing domain. Event
Sourcing pattern suggests to store all changes in the system as a sequence of events.
Event sourcing 230

Instead of having posts table:

PostId, Title, Text, Published, CreatedBy

All changes are stored in post_events table, which is append-only(only insert and
select SQL queries is acceptable):

PostId, EventName, EventDate, EventData

Or in special event stores.


Events:

• PostCreated(Title, Text, CreatedBy)


• PostPublished(PublishedBy)

Well, blog posts are definitely not the right domain for Event Sourcing pattern. The
logic there very rarely depends on the history of posts. Storing the whole application
state as a sequence of events has these advantages:

• developers can “debug” each entity and understand how exactly it went to its
current state.
• whole application state can be calculated for each moment of time.
• any state, based on historical data, can be calculated for each entity, even for
old ones (if created_at and updated_at fields were forgotten, they can be added
later and values will be calculated correctly for each entity).
• any logic, based on historical data, can be implemented and immediately start
to work, even for old entities, not just for new ones. Example, some task tracker
system: if some task was assigned to the same user 3 times - subscribe project
manager to this task.

There are a lot of real industries where the current state is not the most important
data:

• Balance of your bank account is just a cached value of all money transactions
made on this account.
Event sourcing 231

• Insurance calculations are fully based on events that happened before.


• Medical data is always historical.
• Accounting systems are working only with events.

Many industries are regulated and systems have to store audit logs, but if they are
just secondary logs, their correctness is hard to prove. Having all “logs” as a primary
source of data proves their correctness by default.
The real-life technical examples of ES are modern version control systems (like git)
and blockchain technology.
Git stores all data as a sequence of changesets. Changeset contains events like: file
A created with this content, some lines inserted to file B to this position, file C was
deleted.
Blockchain is an append-only list of information blocks, which is linked using
cryptography. Each block contains a cryptographic hash of the previous block, a
timestamp, and transaction data (cryptocurrency transactions or document changes).
Database management systems store all operations that modify data (insert, update,
delete SQL queries) in a special transaction log and sometimes use it as a main
source of data. Replication process between master and slave databases are usually
the transfering of a transaction log and the replaying of all operations there in slave
instance.

Write model unit tests


Let’s take a look again to unit tests for a model written in the Domain layer chapter.
Event sourcing 232

class JobApplyTest extends UnitTestCase


{
public function testApplySameFreelancer()
{
$job = $this->createJob();
$freelancer = $this->createFreelancer();

$freelancer->apply($job, 'cover letter');

$this->expectException(SameFreelancerProposalException::class);

$freelancer->apply($job, 'another cover letter');


}
}

Instead of manually creating an object in the needed state and testing the behavior in
this state, tests have to create this entity in its initial state and run some commands
to get the entity to its needed state. This unit test repeats the ES model.
Events are the only information these models push outside and unit tests can only
check these events. What if some entity will record a successful event but forget to
change its state? If the entity is well covered by tests, some other test will fail. If
Job entity won’t add proposal, SameFreelancer test will fail. In a complex entity
with an enormous amount of possible states, some states might not be tested (do you
remember chess and Monopoly?). Let’s check a very simple case with publishing
posts.

class Post
{
public function publish()
{
if (empty($this->body)) {
throw new CantPublishException();
}

//$this->published = true;
Event sourcing 233

$this->record(new PostPublished($this->id));
}
}

class PublishPostTest extends \PHPUnit\Framework\TestCase


{
public function testSuccessfulPublish()
{
// initialize
$post = new Post('title', 'body');

// run
$post->publish();

// check
$this->assertEventsHas(
PostPublished::class, $post->releaseEvents());
}
}

Test is okay, but published field isn’t updated in database. Our read model won’t
get the proper value and only functional tests will catch this problem (if they were
written for this case). In ES systems, the event itself is a main source, the data which
will be written to the database (or special store). So, unit tests become much more
natural in ES systems. They definitely check the whole behavior.

World without magic


How are entities from the Domain layer chapter stored in a database? Entity classes
there are very closed, all fields are private, and only action methods are public. First,
Doctrine analyzes the php-files with entity class sources, fetches meta information
about entities, fields, and how to map them to the database (it stores this meta-
information cached then). After persist and flush commands, it uses the full power
of dark PHP reflection magic to get all needed values and stores them to database.
Event sourcing 234

What will happen if all magic disappears? In a world without magic, we can’t build
domains like in the Domain layer chapter.
We can forget about Information Hiding and implement getters just to allow a data
mapper to save the entities’ states to a database. Or just implement Event Sourcing!
Events are public. They can be stored in the database instead of the current entities’
state.

It looks very similar to the schema of usual CQRS application. Write model stores
events instead of data needed in the read model. All data needed for the read model
is only the projection of these events:

• Traditional current state of entities in the tables


• Full-text search indices (SphinxSearch or Elasticsearch)
• Special statistics data for reports (sometimes stored in special aggregate tables)
• Other data for reading

Events are the true source of all data the system has generated. Using them as a write
model is very natural, especially if the system is already using a CQRS pattern.
Event sourcing 235

Implementing ES
Today (in the beginning of 2019) the best PHP library to implement an ES pat-
tern is prooph. It also has a perfect demo project, which I want to show here
- prooph/proophessor-do. One of the core concepts of Domain driven design,
Ubiquitous language, is very well presented there by widely used value objects and
named constructors.
Each root entity has the VO for id:

interface ValueObject
{
public function sameValueAs(ValueObject $object): bool;
}

final class TodoId implements ValueObject


{
/** @var UuidInterface */
private $uuid;

public static function generate(): TodoId


{
return new self(Uuid::uuid4());
}

public static function fromString(string $todoId): TodoId


{
return new self(Uuid::fromString($todoId));
}

private function __construct(UuidInterface $uuid)


{
$this->uuid = $uuid;
}
Event sourcing 236

public function toString(): string


{
return $this->uuid->toString();
}

public function sameValueAs(ValueObject $other): bool


{
return \get_class($this) === \get_class($other)
&& $this->uuid->equals($other->uuid);
}
}

TodoId value object just represents UUID value. There is also a ValueObject
interface to compare value objects with each other.

final class TodoWasPosted extends AggregateChanged


{
/** @var UserId */
private $assigneeId;

/** @var TodoId */


private $todoId;

/** @var TodoStatus */


private $todoStatus;

public static function byUser(UserId $assigneeId, TodoText $text,


TodoId $todoId, TodoStatus $todoStatus): TodoWasPosted
{
/** @var self $event */
$event = self::occur(...);

$event->todoId = $todoId;
$event->assigneeId = $assigneeId;
$event->todoStatus = $todoStatus;
Event sourcing 237

return $event;
}

public function todoId(): TodoId {...}

public function assigneeId(): UserId {...}

public function text(): TodoText {...}

public function todoStatus(): TodoStatus {...}


}

An event which creates a todo item. AggregateChanged is the base class for all
ES-events in Prooph. The named constructor is used to make event creation code
look as a natural language sentence: TodoWasPosted::byUser(…). I’ve hidden some
technical stuff, which is not important right now.
Each entity has to extend AggregateRoot class. The main parts of it:

abstract class AggregateRoot


{
/**
* List of events that are not committed to the EventStore
*
* @var AggregateChanged[]
*/
protected $recordedEvents = [];

/**
* Get pending events and reset stack
*
* @return AggregateChanged[]
*/
protected function popRecordedEvents(): array
{
$pendingEvents = $this->recordedEvents;
Event sourcing 238

$this->recordedEvents = [];

return $pendingEvents;
}

/**
* Record an aggregate changed event
*/
protected function recordThat(AggregateChanged $event): void
{
$this->version += 1;

$this->recordedEvents[] =
$event->withVersion($this->version);

$this->apply($event);
}

abstract protected function aggregateId(): string;

/**
* Apply given event
*/
abstract protected function apply(AggregateChanged $event);
}

Here is the same pattern for storing events inside the entity as we used in the Domain
layer chapter. The important difference is the apply method. Entities can change
their state only by applying the event to itself. As you remember, entities’ current
state restores by replaying all events from the beginning, so entities only write the
events with recordThat method and it redirects them to apply method, which does
real changes.
Event sourcing 239

final class Todo extends AggregateRoot


{
/** @var TodoId */
private $todoId;

/** @var UserId */


private $assigneeId;

/** @var TodoText */


private $text;

/** @var TodoStatus */


private $status;

public static function post(


TodoText $text, UserId $assigneeId, TodoId $todoId): Todo
{
$self = new self();
$self->recordThat(TodoWasPosted::byUser(
$assigneeId, $text, $todoId, TodoStatus::OPEN()));

return $self;
}

/**
* @throws Exception\TodoNotOpen
*/
public function markAsDone(): void
{
$status = TodoStatus::DONE();

if (! $this->status->is(TodoStatus::OPEN())) {
throw Exception\TodoNotOpen::triedStatus($status, $this);
}

$this->recordThat(TodoWasMarkedAsDone::fromStatus(
Event sourcing 240

$this->todoId, $this->status, $status, $this->assigneeId));


}

protected function aggregateId(): string


{
return $this->todoId->toString();
}

/**
* Apply given event
*/
protected function apply(AggregateChanged $event): void
{
switch (get_class($event)) {
case TodoWasPosted::class:
$this->todoId = $event->todoId();
$this->assigneeId = $event->assigneeId();
$this->text = $event->text();
$this->status = $event->todoStatus();
break;
case TodoWasMarkedAsDone::class:
$this->status = $event->newStatus();
break;
}
}
}

Little part of the Todo entity. The difference between this entity and entities from
the Domain layer chapter is small, but important: entities’ state fully depends only
on events.
When Prooph has been asked to store entity, it just gets its id and all events to store
by calling popRecordedEvents method and saves them to event store (it might be a
database table or something else). To get an entity in its current state by id, it finds
all events with this id, creates an empty object of needed class and replays all events
one by one with the apply method.
Event sourcing 241

final class Todo extends AggregateRoot


{
/** @var null|TodoDeadline */
private $deadline;

/**
* @throws Exception\InvalidDeadline
* @throws Exception\TodoNotOpen
*/
public function addDeadline(
UserId $userId, TodoDeadline $deadline)
{
if (! $this->assigneeId()->sameValueAs($userId)) {
throw Exception\InvalidDeadline::userIsNotAssignee(
$userId, $this->assigneeId());
}

if ($deadline->isInThePast()) {
throw Exception\InvalidDeadline::deadlineInThePast(
$deadline);
}

if ($this->status->is(TodoStatus::DONE())) {
throw Exception\TodoNotOpen::triedToAddDeadline(
$deadline, $this->status);
}

$this->recordThat(DeadlineWasAddedToTodo::byUserToDate(
$this->todoId, $this->assigneeId, $deadline));

if ($this->isMarkedAsExpired()) {
$this->unmarkAsExpired();
}
}
}
Event sourcing 242

Another part of Todo entity: adding a deadline to a todo item. The first if statement
makes an authorization job and it’s a little violation of the Single Responsibility
Principle, but for a demo project it’s absolutely okay. Just try to read the addDeadline
methods code. It looks like a normal text in natural English, thanks to well-named
constructors and value objects. The deadline object is not just a DateTime, it’s a
TodoDeadline object, with all needed methods, like isInThePast, which make a
client’s code very clean.
I don’t want to go deeper in the model of this demo project. I highly recommend
to review it for everyone who wants to learn how to build an ES application -
https://github.com/prooph/proophessor-do
Projections are special objects which transform events from write model to data for
a read model. Almost all ES systems have obvious projections building traditional
tables with the current state of entities.

final class Table


{
const TODO = 'read_todo';
//...
}

final class TodoReadModel extends AbstractReadModel


{
/**
* @var Connection
*/
private $connection;

public function __construct(Connection $connection)


{
$this->connection = $connection;
}

public function init(): void


{
Event sourcing 243

$tableName = Table::TODO;

$sql = <<<EOT
CREATE TABLE `$tableName` (
`id` varchar(36) COLLATE utf8_unicode_ci NOT NULL,
`assignee_id` varchar(36) COLLATE utf8_unicode_ci NOT NULL,
`text` longtext COLLATE utf8_unicode_ci NOT NULL,
`status` varchar(7) COLLATE utf8_unicode_ci NOT NULL,
`deadline` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL,
`reminder` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_a_status` (`assignee_id`,`status`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
EOT;

$statement = $this->connection->prepare($sql);
$statement->execute();
}

public function isInitialized(): bool


{
$tableName = Table::TODO;

$sql = "SHOW TABLES LIKE '$tableName';";

$statement = $this->connection->prepare($sql);
$statement->execute();

$result = $statement->fetch();

if (false === $result) {


return false;
}

return true;
Event sourcing 244

public function reset(): void


{
$tableName = Table::TODO;

$sql = "TRUNCATE TABLE `$tableName`;";

$statement = $this->connection->prepare($sql);
$statement->execute();
}

public function delete(): void


{
$tableName = Table::TODO;

$sql = "DROP TABLE `$tableName`;";

$statement = $this->connection->prepare($sql);
$statement->execute();
}

protected function insert(array $data): void


{
$this->connection->insert(Table::TODO, $data);
}

protected function update(


array $data, array $identifier): void
{
$this->connection->update(
Table::TODO,
$data,
$identifier
);
}
Event sourcing 245

This class represents a table for storing todo items. init, reset and delete methods
are used when the system needs to create or rebuild the projection. In this class, they
just create and drop/truncate a read_todo table. insert and update methods just do
insert/update SQL queries.
The same class can be created for building/updating a full-text search index, statistics
data, or just logging all events to file (this is not the best way to use projection,
because all events are already stored in event store).

$readModel = new TodoReadModel(


$container->get('doctrine.connection.default'));

$projection = $projectionManager
->createReadModelProjection('todo', $readModel);

$projection
->fromStream('todo_stream')
->when([
TodoWasPosted::class
=> function ($state, TodoWasPosted $event) {
$this->readModel()->stack('insert', [
'id' => $event->todoId()->toString(),
'assignee_id' => $event->assigneeId()->toString(),
'text' => $event->text()->toString(),
'status' => $event->todoStatus()->toString(),
]);
},
TodoWasMarkedAsDone::class
=> function ($state, TodoWasMarkedAsDone $event) {
$this->readModel()->stack(
'update',
[
'status' => $event->newStatus()->toString(),
],
Event sourcing 246

[
'id' => $event->todoId()->toString(),
]
);
},
// ...
])
->run();

This is a configuration of projection. It uses a TodoReadModel class and transforms


the events to commands for this class. TodoWasPosted event will create a new row in
this table. TodoWasMarkedAsDone event will change a status field for the specified
id. After running all events from the beginning, read_todo table will contain the
current state of all todo items in the system. Usual data change workflow for ES
system is: fetching entity (aggregate root) from the event store, calling the command
(markAsDone or addDeadline), getting all events generated by this command
(it may contain more than 1 event), storing them to the event store, calling all
projections, some of them immediately (usually the traditional current state table
projection), some of them by queue.

Unique data in ES systems


One of the disadvantages of Event Sourcing systems is that data in the event store is
impossible to check by constraints like unique indexes. In traditional DBMS, unique
index for the users.email field makes sure that the system will never have users with
the same email. Entities in ES systems are fully independent. Different user entities
with the same email but different id values can live together without any issues, but
system requirements don’t allow this.
Some systems just use a table from the read model to check uniqueness (unique index
there helps), but in case of race condition, this also can allow one to write 2 users
with the same email to the event store (projection obviously will fail trying to insert
the second record). Some systems just create a special table with one unique field
and insert the value there, before storing an event.
Event sourcing 247

Conclusion
Event Sourcing is a very powerful pattern. It allows to easily implement logic based
on historical data. It can help to prove that your “audit log” is correct (for regulated
industries). It also helps systems to be more prepared for changes, especially if the
changes are based on historical data.
Disadvantages are also very big. ES is much more expensive than traditional ways.
For most applications, ES is too heavy and not optimal when compared with the
extracted Domain layer or even “everything in controller” “pattern” which is usually
called MVC. It also requires some skill level from team members. “Thinking by
events” is very different from “thinking by database rows” and each new team
member will spend a lot of time adapting to a project’s style of thinking. Analyzing
demo projects, like proophessor-do, or creating one’s own simple ES projects,
especially with non-standard domains, can help to understand the difficulties of
implementing the ES pattern and to decide whether to use ES in new projects.
13. Sagas
Multi-system “transactions”
I want to make a little chapter about another interesting use of events. I already
wrote about database transactions, which allow us to have consistent data. They
execute all queries inside, or roll them back. What if our system is big and contains
several subsystems with their own databases? Each subsystem can be written using a
different framework or even programming language. How can one make transactions
between these subsystems? The Saga pattern solves this problem.
Saga is a sequence of local transactions and events, generated after each of them.
Let’s imagine some example. Air tickets selling system. Subsystems are:

• Order system with user interface (usually they are different systems)
• Aircraft seats booking
• Payment processing

Successful Saga:

1. Order system creates a new order.


2. Seat booking system reserves the seats in aircraft.
3. Order system asks user to enter payment details (card, for example).
4. Payment system processes the payment.
5. Seat booking system marks reserved seats as booked.
6. Order system marks the order as successful.

Systems have to talk with each other. Events are a good choice for that. They can be
pushed to message queue systems (like RabbitMQ or Apache Kafka) and processed
by subsystems listeners there.
Sagas 249

Successful Saga with events:

1. Order system creates a new order (status=pending) in its own database and
generates OrderCreated event.
2. Seat booking system catches this event and reserves the seats in aircraft.
SeatsReserved event.
3. Order system catches this event and asks user to enter payment details (card,
for example). PaymentDetailsSet event.
4. Payment system processes the payment. PaymentProcessed event.
5. Order system marks the order as payed. OrderPayed event.
6. Seat booking system marks reserved seats as booked. SeatsBooked event.
7. Order system marks the order as successful.

Each event contains the OrderId value to identify the order in each system.
Each step might be failed. Seats might be reserved by someone else. User can cancel
the order at any step. Payment might be processed wrong. If Saga was failed in some
Sagas 250

step, all previous actions should be reverted. Reserved or booked seats should be
freed. Money should be returned.
It should be implemented by compensating actions and *Failed events. If payment
was failed, PaymentFailed event will be fired. Seat booking system reacts to this
event and frees the seats. Order system marks this order as failed.
UserCancelled event can be fired during each step by user. All systems will make
compensation actions.
Saga is a high-level transaction. Each *Failed event causes a rollback action and the
system returns to its initial state.

Orchestration
I see at least two problems in this Saga. Systems know too much about each other.
Seat booking system should react to PaymentFailed event of payment system. And
both them should react to UserCancelled UI event. It doesn’t look like a big problem
in this simple example, but real Sagas might have hundreds of possible events. Each
change there can result in changes in many other systems. Can you imagine how
difficult it might be to find all usages of some event? “Find usages” IDE feature
doesn’t work well in multi-system applications.
Another problem is that some *Failed events shouldn’t rollback the whole Saga.
Order system can ask user to choose another payment method on PaymentFailed
event. It will be like a “partial rollback”. Implementing this will increase the
complexity of the Saga and will touch each system.
This Saga coordination way with pure events is called “Choreography-based Saga”.
Another way to coordinate Sagas is “Orchestration-based Saga”. An additional object
- orchestrator - makes all coordination between systems and they don’t know about
each other. Orchestrator might be an additional system or part of one of the Saga
participants (in the order system, for example).
Sagas 251

Orchestrator catches OrderCreated event and asks seat booking system to reserve
the seats. After SeatsReserved event, it asks Order system to make needed ac-
tions and so on. Systems in Orchestration-based Sagas are fully independent and
implement only its own responsibilities, leaving “event-reaction” functionality to
orchestrator.
Sagas can solve a large number of issues of implementing business transactions by
multiple systems, but they are, as each multi-system solution, very hard to support.
Orchestrator reduces the dependencies between systems, which helps a lot, but the
whole system will always be complex. Teams use visualisations like block diagrams
to describe each Saga to have a full understanding how it works, but even diagrams
become too complicated for some Sagas. Some projects are just too big and will
always be complex.
14. Useful books and links
As I said in the beginning of this book, most patterns and techniques were observed
superficially. This chapter contains some books and links which can help you to
understand some of them better.

Classic
• “Clean Code” by Robert Martin
• “Refactoring: Improving the Design of Existing Code” by Martin Fowler

DDD
• “Domain-Driven Design: Tackling Complexity in the Heart of Software”
by Eric Evans
• “Implementing Domain-Driven Design” by Vaughn Vernon

ES and CQRS
• https://aka.ms/cqrs - CQRS journey by Microsoft
• https://github.com/prooph/proophessor-do - Prooph library example project

Unit testing
• F.I.R.S.T Principles of Unit Testing

You might also like