Vadim Samokhin

@wrong.about

You Don’t Need a Domain Service Class in DDD

You don’t need an Application service class either!

Let’s check the definition made by the author of the concept:

When a significant process or transformation in the domain is not a natural responsibility of an ENTITY or VALUE OBJECT, add an operation to the model as standalone interface declared as a SERVICE. Define the interface in terms of the language of the model and make sure the operation name is part of the UBIQUITOUS LANGUAGE. Make the SERVICE stateless.
Eric Evans, Domain-Driven Design

Service class is procedural

In plain English it means that it’t nothing more then a procedure. Why? Because you take an object, operate upon its data and pass it to another object. It contradicts to basic principles of OOP, where data and behavior reside together. So in my object world everything is some object’s responsibility.
Typical example of procedural code that involves a domain service is password hashing.

Use decorators instead

Every time you need some new behavior, you should consider using a decorator pattern. So that’s how the example above can be re-written:

try {
echo
(new HashedPassword(
new RegexMatchedPassword(
new NonEmptyPassword(
new Password('asdSd12@A1')
),
new Regex('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,10}$/')
)
))
->string()
;
} catch (Exception $exception) {
echo $exception->getMessage();
}

All attention to ‘HashedPassword’ class. All hashing functionality resides there. I like this name — hashed password. It’s very declarative, unlike “hash password”, which is more like “how”, than “what”, thus being inherently imperative.

Besides moving domain service’s functionality to DDD’s value object, I employed validation decorators, the concept introduced by Yegor Bugayenko.

So here is the rest of the code:

interface IPassword
{
public function string();
}

class Password implements IPassword
{
private $password;

public function __construct($password)
{
$this->password = $password;
}

public function string()
{
return $this->password;
}
}

class NonEmptyPassword implements IPassword
{
private $password;

public function __construct(IPassword $password)
{
$this->password = $password;
}

public function string()
{
if (is_null($this->password->string())) {
throw new Exception('Password can not be empty');
}

return $this->password->string();
}
}

class RegexMatchedPassword implements IPassword
{
private $password;
private $regex;

public function __construct(IPassword $password, IRegex $regex)
{
$this->password = $password;
$this->regex = $regex;
}

public function string()
{
// https://stackoverflow.com/a/21456918/618020
if (!preg_match($this->regex->value(), $this->password->string())) {
throw new Exception(
'Password must have minimum eight and maximum 10 characters, at least one uppercase letter,
one lowercase letter, one number and one special character.'
);
}

return $this->password->string();
}
}

interface IRegex
{
public function value();
}

class Regex implements IRegex
{
private $regex;

public function __construct($regex)
{
$this->regex = $regex;
}

public function value()
{
if (preg_match($this->regex, null) === false) {
throw new Exception('Your regex is broken.');
}

return $this->regex;
}
}

class HashedPassword implements IPassword
{
private $password;

public function __construct(IPassword $password)
{
$this->password = $password;
}

public function string()
{
return sha1($this->password->string());
}
}

So if objects are designed properly, there are no services. There are just normal objects, representing the real things. So following this heuristics, I claim that Application services are a mistake too.

Stay tuned.

More by Vadim Samokhin

Topics of interest

More Related Stories