Domain Driven Design with Web API revisited Part 15: the concrete application service

Introduction

In the previous post we looked at application services in general. We saw that they connect the ultimate consumer layer, such as MVC with the domain layer. They have no or very little logic. Instead, they act as coordinators. They accept requests from the consumer layer and make sure that the consumer layer gets back a valid response. The consumer won’t care how the application service carries out its job and what concrete dependencies it may have.

In this post we’ll build upon the abstract timetable service and basic messaging classes. The goal is to build a functioning service layer.

The concrete application service

Open the demo load test application we’ve been working on. Locate the WebSuiteDemo.Loadtesting.ApplicationServices project we inserted in the previous post. Add a folder called Implementations. Insert a class called TimetableService into the folder and let it implement the ITimetableService interface. Let Visual Studio add the usual “not implemented” messages for you.

Now we need to think about the dependencies that the service will require in order to execute the required methods. It will need to communicate with the load test repository to add, update, delete and show load tests. Furthermore it will need to convert between Loadtest domains and Loadtest view models. We have two repositories for that purpose: one of type ITimetableRepository and another one of type ITimetableViewModelRepository. The concrete TimetableService class will have those two dependencies to begin with.

Add the following private fields and constructor to the file:

private readonly ITimetableRepository _timetableRepository;
private readonly ITimetableViewModelRepository _timetableViewModelRepository;

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

Next we need to connect the application service method implementations with the repository methods. However, the repository methods are not awaitable, i.e. they are “normal” synchronous functions. We could transform ITimetableRepository and ITimetableViewModelRepository as well but let’s pretend that we have no access to that interface.

One possible solution is to build private methods in the application service class that call the repository. The public method implementations will in turn call upon those private methods on separate threads using the Task class which will make the chain awaitable. We’ll soon see what all this means.

Let’s start with the load test deletion logic as that’s the most straightforward. Add the following method to TimetableService:

private DeleteLoadtestResponse DeleteLoadtest(DeleteLoadtestRequest deleteLoadtestRequest)
{
	DeleteLoadtestResponse resp = new DeleteLoadtestResponse();
	try
	{
		_timetableRepository.DeleteById(deleteLoadtestRequest.Id);
	}
	catch (Exception ex)
	{
		resp.Exception = ex;
	}
	return resp;
}

That should be quite easy to follow. We retrieve the Id from the request and delegate the actual deletion to the repository. Here’s the implementation of the public DeleteLoadtestAsync method:

public async Task<DeleteLoadtestResponse> DeleteLoadtestAsync(DeleteLoadtestRequest deleteLoadtestRequest)
{
	return await Task<DeleteLoadtestResponse>.Run(() => DeleteLoadtest(deleteLoadtestRequest));
}

We’ll follow the same pattern for the retrieval and add/update methods.

Let’s now turn our attention to the load test retrieval method. The following private method will get the job done:

private GetLoadtestsForTimePeriodResponse GetLoadtestsForTimePeriod(GetLoadtestsForTimePeriodRequest getLoadtestsForTimePeriodRequest)
{
	GetLoadtestsForTimePeriodResponse resp = new GetLoadtestsForTimePeriodResponse();
	try
	{
		IList<Loadtest> loadtests = _timetableRepository.GetLoadtestsForTimePeriod(getLoadtestsForTimePeriodRequest.SearchStartDateUtc, getLoadtestsForTimePeriodRequest.SearchEndDateUtc);
		IEnumerable<LoadtestViewModel> ltVms = _timetableViewModelRepository.ConvertToViewModels(loadtests);
		resp.Loadtests = ltVms;
	}
	catch (Exception ex)
	{
		resp.Exception = ex;
	}
	return resp;
}

We let the ITimetableRepository handle the actual load test retrieval. ITimetableViewModelRepository will be responsible for the data transformation so that the caller will see the readable names of the domain objects and not just some Guid. Here’s the corresponding implementation of the public GetLoadtestsForTimePeriodAsync method:

public async Task<GetLoadtestsForTimePeriodResponse> GetLoadtestsForTimePeriodAsync(GetLoadtestsForTimePeriodRequest getLoadtestsForTimePeriodRequest)
{
	return await Task<GetLoadtestsForTimePeriodResponse>.Run(() => GetLoadtestsForTimePeriod(getLoadtestsForTimePeriodRequest));
}

Finally we have the add/update method implementation. Add the following private method to the class:

private AddOrUpdateLoadtestsResponse AddOrUpdateLoadtests(AddOrUpdateLoadtestsRequest addOrUpdateLoadtestsRequest)
{
	AddOrUpdateLoadtestsResponse resp = new AddOrUpdateLoadtestsResponse();
	try
	{
                //assign ID if not present, assume to be insertion
		foreach (LoadtestViewModel ltvm in addOrUpdateLoadtestsRequest.Loadtests)
		{
			if (ltvm.Id == null || ltvm.Id == default(Guid))
			{
				ltvm.Id = Guid.NewGuid();
			}
		}
		List<LoadtestViewModel> sortedByDate = addOrUpdateLoadtestsRequest.Loadtests.OrderBy(lt => lt.StartDateUtc).ToList();
                LoadtestViewModel last = sortedByDate.Last();
		IList<Loadtest> loadtests = _timetableRepository.GetLoadtestsForTimePeriod(sortedByDate.First().StartDateUtc, last.StartDateUtc.AddSeconds(last.DurationSec));
		Timetable timetable = new Timetable(loadtests);		
		IList<Loadtest> loadtestsAddedOrUpdated = _timetableViewModelRepository.ConvertToDomain(addOrUpdateLoadtestsRequest.Loadtests);
		AddOrUpdateLoadtestsValidationResult validationResult = timetable.AddOrUpdateLoadtests(loadtestsAddedOrUpdated);
		_timetableRepository.AddOrUpdateLoadtests(validationResult);
		resp.AddOrUpdateLoadtestsValidationResult = validationResult;
	}
	catch (Exception ex)
	{
		resp.Exception = ex;
	}
	return resp;
}

First we iterate through the view models and check the status of their IDs. If no ID was given or the ID is equal to the default Guid value of all zero’s then we assign a brand new ID. We therefore assume that the caller meant an insertion. In case of an update the caller will provide the Guid of an existing load test. Next we sort the incoming view models by date. We’ll need to build a Timetable object so that it can run its validation logic. We follow the same process we saw in the post where we tested the actual repository implementations through a C# console application. In order to build a Timetable object we also need the list of load tests that were already booked for the time frame of the incoming load test requests. We’ll simply get that range for the earliest start date and latest end date in the incoming list of load test view models, that should be enough for the validation logic. We convert the load test view models into load test domains and provide that list to the Timetable.AddOrUpdateLoadtests method. Then we let the ITimetableRepository object take care of the actual addition and update operation. Finally we assign the AddOrUpdateLoadtestsValidationResult object from Timetable to the AddOrUpdateLoadtestsResponse object that the AddOrUpdateLoadtests returns.

We can now implement the last public async method of the TimetableService class:

public async Task<AddOrUpdateLoadtestsResponse> AddOrUpdateLoadtestsAsync(AddOrUpdateLoadtestsRequest addOrUpdateLoadtestsRequest)
{
	return await Task<AddOrUpdateLoadtestsResponse>.Run(() => AddOrUpdateLoadtests(addOrUpdateLoadtestsRequest));
}

We’ll see all of this in action in the last post of the series.

We’re done with the concrete application service. In the next post we’ll start building the topmost layer in our solution, i.e. the Web API which will consume the services offered by the application service.

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.

One Response to Domain Driven Design with Web API revisited Part 15: the concrete application service

  1. Mark says:

    I realise this post is almost two years old, but your advice to use Task.Run(…) in the implementation of the service methods (eg AddOrUpdateLoadtestsAsync) isn’t a good idea – see this SO answer https://stackoverflow.com/a/29168586 and https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html for details.

    Loved the series otherwise though!

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: