paint-brush
You Don’t Need a Domain Service Class in DDDby@vadim-samokhin
12,193 reads
12,193 reads

You Don’t Need a Domain Service Class in DDD

by Vadim SamokhinAugust 15th, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

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

Company Mentioned

Mention Thumbnail
featured image - You Don’t Need a Domain Service Class in DDD
Vadim Samokhin HackerNoon profile picture

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.