Application Services

Application Services are the middleware between the outside world and the Domain logic. The purpose of such a mechanism is to transform commands from the outside world into meaningful Domain instructions.

Let's consider the User signs up to our platform use case. Starting with an outside-in approach: from the delivery mechanism, we need to compose the input request for our Domain operation. Using a framework like Symfony as the delivery mechanism, the code would look something like this:

class SignUpController extends Controller
{
public function signUpAction(Request $request)
{
$signUpService = new SignUpUserService(
$this->get('user_repository')
);

try {
$response = $signUpService->execute(new SignUpUserRequest(
$request->request->get('email'),
$request->request->get('password')
));
} catch (UserAlreadyExistsException $e) {
return $this->render('error.html.twig', $response);
}

return $this->render('success.html.twig', $response);
}
}

As you can see, we create a new instance of our Application Services, passing all dependencies needed — in this case, a UserRepository. UserRepository is an interface that can be implemented with any specific technology (Example: MySQL, Redis, Elasticsearch). Then, we build a request object for our Application Service in order to abstract the delivery mechanism — in this example, a web request — from the business logic. Last, we execute the Application Service, get the response, and use that response for rendering the result. On the Domain side, let's check a possible implementation for the Application Service that coordinates the logic that fulfills the User signs up use case:

class SignUpUserService
{
private $userRepository;

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

public function execute(SignUpUserRequest $request)
{
$user = $this->userRepository->userOfEmail($request->email);
if ($user) {
throw new UserAlreadyExistsException();
}

$user = new User(
$this->userRepository->nextIdentity(),
$request->email,
$request->password
);

$this->userRepository->add($user);

return new SignUpUserResponse($user);
}
}

Everything in the code is about the Domain problem we want to solve, and not about the specific technology we're using to solve it. With this approach, we can decouple the high-level policies from the low-level implementation details. The communication between the delivery mechanism and the Domain is carried by data structures called DTOs, which we introduced in the Chapter 2Architectural Styles:

class SignUpUserRequest
{
public $email;
public $password;

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

There are different strategies for returning content, but for now, consider that we shouldn't return our Entities so that they can't be modified from outside our Application Services. That's why it's common to return another DTO with information, rather than the whole Entity. Let's see a simple example:

class SignUpUserResponse
{
public $id;
public $email;

public function __construct(User $user)
{
$this->id = $user->id();
$this->email = $user->email();
}
}

For creating your responses, you can use getters or public instance variables. Application Services should take care with transaction scopes and security. However, you'll delve into more detail about these and other things related to Application Services in the Chapter 11Application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset