Domain Driven Design with Web API extensions part 1: notifications

Introduction

A couple of weeks ago we went through an updated series on Domain Driven Design starting with this post. We built a functioning skeleton project with EntityFramework as the backing store, a Web API layer as the top consumer, a loosely coupled service layer and a central domain layer with some logic.

In this extension series we’ll investigate a couple of ways to add various dependencies to the project. In particular we’ll look into how to send email notifications in case a new load test has been booked.

This sounds very much like an easy task and it’s certainly not the most difficult one programming professionals encounter. I chose this scenario as it’s simple enough to demonstrate some key concepts from software architecture and DDD.

Independent and loosely coupled components

Whenever you extend a project you should try do it with tried and tested software architecture principles in mind. There might already be some extension points, such as abstractions that you can derive from or implement. It’s probably close to impossible to achieve 100% SOLID in practice, but you should still strive to aim as high as possible. Your life will be a lot easier later on when customers demand new features at a quick pace.

There are various series on this blog dedicated to software architecture if you want to learn more – I’m not planning to regurgitate everything that’s already been discussed:

In summary well thought-out software design will have the following advantages among others:

  • Testability: with lean classes and methods your automated tests will provide unambiguous results. With large methods and various tight dependencies you may not be sure why a test has failed
  • Flexibility: replace components with new ones at ease instead of modifying existing code in various places
  • Single responsibility: strictly speaking every method should carry out a single, well defined action on its own. It’s OK if a class or method delegates some of its work to the – loosely coupled – dependencies. However, you shouldn’t have giant methods that check some status in the database, send emails, calculate the new price etc. all implemented within its method body

Emailing and infrastructure

The project where we left off can be downloaded from GitHub here if you didn’t follow along the original series. Here’s the project structure at this point:

Final project structure before first extension

The first new concept we’ll build into the project is the infrastructure layer. We had one in the original DDD series. Let’s recap on what the purpose of an infrastructure layer is:

The infrastructure layer is a place for all sorts of cross-cutting concerns and objects that can be used in any project. They are not specific to any single project or domain. Examples: logging, file operations, security, caching, helper classes for Date and String operations etc. Putting these in a separate infrastructure layer helps if you want to employ the same logging, caching etc. policy across all your projects. You could put these within the project solution but then when you start your next project then you may need to copy and paste a lot of code.

In practice the infrastructure layer will be a normal C# library. If your projects are stored in GitHub then the infra layer can be shared among the various projects by linking it as sub-modules. We won’t see how to do that, this was just a tip.

Add a new C# library called WebSuiteDDD.Infrastructure.Common. Remove Class1 and add a folder called Emailing. I will reuse some of the elements outlined in the emailing section of the object dependencies series in a reduced form.

Add the following class first which will contain the result of sending an email:

public class EmailSendingResult
{
	public bool EmailSentSuccessfully { get; set; }
	public string EmailSendingFailureMessage { get; set; }
}

The following object will hold the properties of the email message:

public class EmailArguments
{
	private string _subject;
	private string _message;
	private string _to;
	private string _from;
	private string _smtpServer;

	public EmailArguments(string subject, string message, string to, string from, string smtpServer)
	{
		if (string.IsNullOrEmpty(subject))
			throw new ArgumentNullException("Email subject");
		if (string.IsNullOrEmpty(message))
			throw new ArgumentNullException("Email message");
		if (string.IsNullOrEmpty(to))
			throw new ArgumentNullException("Email recipient");
		if (string.IsNullOrEmpty(from))
			throw new ArgumentNullException("Email sender");
		if (string.IsNullOrEmpty(smtpServer))
			throw new ArgumentNullException("Smtp server");
		this._from = from;
		this._message = message;
		this._smtpServer = smtpServer;
		this._subject = subject;
		this._to = to;
	}

	public string To
	{
		get
		{
			return this._to;
		}
	}

	public string From
	{
		get
		{
			return this._from;
		}
	}

	public string Subject
	{
		get
		{
			return this._subject;
		}
	}

	public string SmtpServer
	{
		get
		{
			return this._smtpServer;
		}
	}

	public string Message
	{
		get
		{
			return this._message;
		}
	}
}

That’s a simple “container” object with no special logic attached. Finally we have the emailing interface:

public interface IEmailService
{
	EmailSendingResult SendEmail(EmailArguments emailArguments);
}

We will only simulate the actual email sending so let’s go for the below implementation. Insert the following class into the Emailing folder:

public class FakeEmailService : IEmailService
{
	public EmailSendingResult SendEmail(EmailArguments emailArguments)
	{
		string message = string.Format("From: {0}, to: {1}, message: {2}, server: {3}, subject: {4} ",
			emailArguments.From, emailArguments.To, emailArguments.Message, emailArguments.SmtpServer, emailArguments.Subject);
		Debug.WriteLine(message);
		return new EmailSendingResult() { EmailSendingFailureMessage = "None", EmailSentSuccessfully = true };
	}
}

That should be simple to follow.

How do we add this new service to our solution?

We’ll first go for a suboptimal, but still “half OK” solution that you’ll probably often see in real life projects.

It seems logical that the TimetableService class should take care of sending the email after the following call in the private AddOrUpdateLoadtests method:

resp.AddOrUpdateLoadtestsValidationResult = validationResult;

Let’s see where it leads us. Note that we will eventually undo the changes made to TimetableService.cs in the next post.

Add a project reference to the infrastructure layer in the application services layer. Then extend the private fields and constructor of TimetableService as follows:

private readonly ITimetableRepository _timetableRepository;
private readonly ITimetableViewModelRepository _timetableViewModelRepository;
private readonly IEmailService _emailService;

public TimetableService(ITimetableRepository timetableRepository, 
	ITimetableViewModelRepository timetableViewModelRepository, IEmailService emailService)
{
	if (timetableRepository == null) throw new ArgumentNullException("TimetableRepository");
	if (timetableViewModelRepository == null) throw new ArgumentNullException("TimetableViewModelRepository");
	if (emailService == null) throw new ArgumentNullException("EmailService");
	_timetableRepository = timetableRepository;
	_timetableViewModelRepository = timetableViewModelRepository;
	_emailService = emailService;
}

Then locate…

resp.AddOrUpdateLoadtestsValidationResult = validationResult;

…in the method…

private AddOrUpdateLoadtestsResponse AddOrUpdateLoadtests(AddOrUpdateLoadtestsRequest addOrUpdateLoadtestsRequest)

…and add the following bit of code:

if (!validationResult.Failed.Any())
{
	EmailArguments args = new EmailArguments("Load tests added or updated", "Load tests added or updated", "The Boss", "The developer", "123.456.678");
	_emailService.SendEmail(args);
}

We have now added an extra dependency to TimetableService. Recall that we have set up StructureMap to take care of our dependencies. In this case we don’t follow the default interface-implementation naming rule, e.g. IService – Service, so we have to specify in code what concrete type we want StructureMap to insert for us.

Add a project reference to the infrastructure layer from the WebApi layer. Then open the DefaultRegistry.cs file in the DependencyResolution folder. It should have an example commented out after the call to the Scan method:

//For<IExample>().Use<Example>();

Add the following code after that comment:

For<IEmailService>().Use<FakeEmailService>();

We’re done with the code extensions. Start the web service and try to insert a new load test the same way we did here using a simple console application.

If all goes well then you should see a message similar to this in the Debug window:

From: The developer, to: The Boss, message: Load tests added or updated, server: 123.456.678, subject: Load tests added or updated

…meaning that the email sending implementation has been called.

Current state

Is this a good solution? Well, it has at least a couple of good points:

  • The public interface of TimetableService tells us that it will need an email service, i.e. the dependency is not hidden somewhere in the implementation
  • Therefore we can add some empty implementation of IEmailService if we don’t want to send any email, check out the Null object pattern
  • In fact we can insert any implementation of IEmailService, TimetableService is not tightly coupled to any concrete class

What are the disadvantages?

  • AddOrUpdateLoadtests really should not include the email sending logic: its scope has been extended with an activity that has not much to do with adding and updating load tests in the data store
  • AddOrUpdateLoadtests is now trying to perform too much. We can still claim that the method doesn’t directly send the email, which would be the absolutely worst solution. Instead it delegates the action to an abstraction. However, this task may not be obvious to a the external caller.

In this next post we’ll see how to improve this code using the decorator pattern.

View the list of posts on Architecture and Patterns here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

3 Responses to Domain Driven Design with Web API extensions part 1: notifications

  1. Jan Hansen says:

    I was thinking “Domain Events” all the way through this article until you wrote “decorator pattern”… 🙂

    Would you consider doing a writeup on Domain Events?

    Thanks for your hard work on all these articles!

  2. Pingback: Domain Driven Design with Web API extensions part 1: notifications | Dinesh Ram Kali.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: