Oct 06, 2014
Table of contents:
A typical Domain Driven Design application will be comprised of three main areas. These three areas are the Domain, the Infrastructure and the Application.
Each service layer acts as a boundary to the set of operations that are available inside that layer of abstraction. Each layer has a strict set of responsibilities and should not be concerned with operations outside of their line of responsibility.
Last week we started to look at Domain Services. A Domain Service should model functionality that is derived from the ubiquitous language of the application, but does not logically fit on any existing domain object.
This week we’re going to look at the first of many Infrastructure services we’re going to require in building Cribbb. Sending emails to users is going to be an important bit of functionality, and so today we’re going to look at building a Mailer Infrastructure Service.
Before I jump into building the Mailer Infrastructure Service, first I will briefly recap what are Services in Domain Driven Design, and why do we need them.
There are three types of Service in Domain Driven Design, these are Domain Services, Application Services and Infrastructure Services.
As we saw last week, a Domain Service encapsulates business logic that does not naturally fit on any existing domain object. For example, a User
requires a hashed password, but it is not the responsibility of the User
to hash the password. Instead we can move this functionality to a HashingService
.
An Application Service should handle the requests and responses from the outside world. The Application Service will orchestrate the various domain objects and services required to fulfil the request.
And finally, an Infrastructure Service should be used to abstract the technical details of the application. For example if we needed message queues, an email provider or a payment gateway, we would use an Infrastructure Service to provide that functionality.
Infrastructure Services are used to abstract the technical concerns and implementations of the outside world. It is not the concern of your application for how message queues are fulfilled or how your email is sent.
Each Infrastructure Service should implement an interface that acts as a contract. The interface should define a public API of methods that should be implemented by the Infrastructure Service.
Each technical implementation of an Infrastructure Service sits outside of the boundary of our application and so our application code should depend on the interface contract, and not the concrete implementation.
This is important for a couple of reasons.
Firstly, it ensures that your application does not depend on any outside service. Each implementation of the Infrastructure Service should be replaceable with another implementation that satisfies the interface contract.
Secondly, it ensures that your code is easier to test. When you are testing your code you don’t want to actually fire off requests to a third-party email provider. Instead you can simply mock the email provider by providing an implementation that satisfies the same interface contract.
You might be thinking, “why don’t you just use Laravel’s Mailer class?”.
I think I’ve used Laravel’s Mailer class in every Laravel project I’ve ever worked on. One of the best things about Laravel’s Mailer class is the driver-based providers. It’s very easy to switch email providers by simply changing a configuration setting.
In theory I could just wrap Laravel’s Mailer class in my own interface and use it as an implementation.
However I’m not going to do that for a couple of reasons.
Firstly, it feels a bit weird to have a Laravel implementation that can then be used with any number of email providers. I don’t think this kind of double abstraction is going to do me any favours. I much prefer having simple concrete implementations for each email provider that I write myself.
Secondly, Laravel’s Mailer class is also dependent on the View class. This is fantastic for when you are using it within the context of a Laravel application because it makes it really easy to send email. However I don’t consider Cribbb to be a Laravel application and so I don’t want to have dependencies hidden within implementations. I want to keep the functionality of sending an email completely separate from the functionality of loading the raw HTML.
I think Laravel’s Mailer class is fantastic and I will continue to use it in other projects, however it’s just not right for this project.
So now that I’ve explained how Infrastructure Services work and why they are so important, we can get down to building the Mailer Service.
This code will live under the main namespace of Infrastructure
and will have it’s own namespace of Mailer
.
The first thing I’m going to do will be to write an interface for a Message
implementation. When we send something through the mailer, the object should implement this Message
interface. This is so we know that whatever object is passed in will always implement the Message
contract and will have the appropriate methods available.
Here is the Message
interface:
<?php namespace Cribbb\Infrastructure\Mailer;
interface Message
{
/**
* Return the recipient
*
* @return string
*/
public function to();
/**
* Return the sender
*
* @return string
*/
public function from();
/**
* Return the subject
*
* @return string
*/
public function subject();
/**
* Return the body
*
* @return string
*/
public function body();
}
We will also have an EmailMessage
implementation:
<?php namespace Cribbb\Infrastructure\Mailer;
class EmailMessage implements Message
{
/**
* The recipient
*
* @param string
*/
private $to;
/**
* The sender
*
* @param string
*/
private $from;
/**
* The subject
*
* @param string
*/
private $subject;
/**
* The body of the message
*
* @param string
*/
private $body;
/**
* Create a new Message
*
* @param string $to
* @param string $from
* @param string $subject
* @param string $body
*/
public function __construct($to, $from, $subject, $body)
{
$this->to = $to;
$this->from = $from;
$this->subject = $subject;
$this->body = $body;
}
/**
* Return the recipient
*
* @return string
*/
public function to()
{
return $this->to;
}
/**
* Return the sender
*
* @return string
*/
public function from()
{
return $this->from;
}
/**
* Return the subject
*
* @return string
*/
public function subject()
{
return $this->subject;
}
/**
* Return the body
*
* @return string
*/
public function body()
{
return $this->body;
}
}
Now in theory we could send any type of message along as it implements the Message
interface.
A lot of people will tell you it’s bad practice to have an interface with only one implementation. However I think that is total nonsense. An interface is simply a contract that your code should depend on, it doesn’t matter how many implementations you have.
Next we will write the Mailer
interface. The Mailer
interface should have a single public send()
method:
<?php namespace Cribbb\Infrastructure\Mailer;
interface Mailer
{
/**
* Send a Message
*
* @param Message $message
* @return void
*/
public function send(Message $message);
}
Whenever we need to use the mailer service, the only thing we are going to need to do is to send a message. This is the single responsibility of this class. We don’t care how the email is actually sent, we only care that this single method is available.
And finally we can write the MailGun
implementation:
<?php namespace Cribbb\Infrastructure\Mailer;
class MailGunMailer implements Mailer
{
/**
* Send a Message
*
* @param Message $message
* @return void
*/
public function send(Message $message)
{
// Use the MailGun SDK to send the email
}
}
I’m not going to finish of this concrete implementation off at the moment because I’ve got bigger fish to fry. We’ve already set up the interface and the concrete class, so when I need to start sending emails I can just implement the internals of this class.
When I do come to finish off this class I will inject an instance of the MailGun SDK in through the constructor and then implement the send()
method to use the SDK and make a request to the MailGun API.
Because we’ve written the MailGunMailer
concrete class using the Mailer
interface we don’t need to actually implement the class until we really need to send an email. We’ve already got everything in place to use this contract in tests or in other components of the application.
Infrastructure Services are an extremely important aspect of your application. We need them to run the various jobs of an application and to co-ordinate the various services outside the scope of our application.
Nearly all web applications will require access to a database, or the ability to send emails. Infrastructure Services deal with these technical details that are not really the responsibility of your particular application.
However, at the end of the day, we don’t have to really care about how these Infrastructure Services work. Our applications should not be concerned whether we are using MailGun or SendGrid as the email provider.
The Infrastructure layer of your application should be insulated using interface contracts. Instead of coupling ourselves to these concrete classes, we can use interfaces as boundries.
This not only allows you to easily swap out implementations, but it also makes testing your code a whole lot easier.
However I think the biggest advantage of this pattern of programming is purely the fact that you are writing explicit interfaces and a public API for your application’s classes. This kind of explicit programming will make building and maintaining your application a whole lot easier.
As I mentioned earlier in this post, I love Laravel’s Mailer class and I will continue to use it in nearly all of my Laravel projects. However I think in certain situations you can get a lot of value by writing these kind of Infrastructure Services for yourself.
This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.