Welcome to the seventeenth episode of my course “Becoming a software developer” in which we will mostly talk about the boundaries and responsibilities of the application services. Eventually, we will implement some helper code to automatically assign the authenticated user id to the given command.
All of the materials including videos and sample projects can be downloaded from here.
The source code repository is being hosted on GitHub.
Scope
- Boundaries
- AuthenticatedCommand
Abstract
Boundaries
Defining the boundaries of the application services is not an easy task. We have to keep in mind that accordingly to the SRP (Single Responsibility Principle) and ISP (Interface Segregation Principle) we should define our interfaces to revolve around the particular topic (e.g. managing users, defining routes, finding location etc.). If our service defines additional responsibilities, which are not a part of its scope, most likely we should implement a separate class.
Let’s take a look at the IDriverRouteService which is all about managing available routes for the drivers and passengers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public async Task AddAsync(Guid userId, string name, double startLatitude, double startLongitude, double endLatitude, double endLongitude) { var driver = await _driverRepository.GetOrFailAsync(userId); var startAddress = await _routeManager.GetAddressAsync(startLatitude, startLongitude); var endAddress = await _routeManager.GetAddressAsync(endLatitude, endLongitude); var startNode = Node.Create(startAddress, startLatitude, startLongitude); var endNode = Node.Create(endAddress, endLatitude, endLongitude); var distance = _routeManager.CalculateLength(startLatitude, startLongitude, endLatitude, endLongitude); driver.AddRoute(name, startNode, endNode,ha distance); await _driverRepository.UpdateAsync(driver); } |
Even though the route requires the actual address (e.g. fetched from some specific API like Google Maps) and the distance between nodes in order to create a valid Route object, it does not do it on its own. Instead, it makes use of the available service defined as IRouteManager which exposes the required methods:
1 2 3 4 5 6 |
public interface IRouteManager : IService { Task<string> GetAddressAsync(double latitude, double longitue); double CalculateLength(double startLatitude, double startLongitude, double endLatitude, double endLongitude); } |
If our IDriverRouteService would require additional workflow that is not particularly tied to the route itself (such as fetching the address), we could simply define additional interfaces and inject them via constructor in order to extend the business logic.
AuthenticatedCommand
Let’s say, we would like to have automatically assigned user id, whenever there’s a command which requires the user to be authenticated. It can be done easily in 3 steps:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public interface IAuthenticatedCommand : ICommand { Guid UserId { get; set; } } public class AuthenticatedCommandBase : IAuthenticatedCommand { public Guid UserId { get; set; } } public class CreateDriver : AuthenticatedCommandBase { public DriverVehicle Vehicle { get; set; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
[Route("[controller]")] public abstract class ApiControllerBase : Controller { private readonly ICommandDispatcher CommandDispatcher; protected Guid UserId => User?.Identity?.IsAuthenticated == true ? Guid.Parse(User.Identity.Name) : Guid.Empty; protected ApiControllerBase(ICommandDispatcher commandDispatcher) { CommandDispatcher = commandDispatcher; } protected async Task DispatchAsync<T>(T command) where T : ICommand { if(command is IAuthenticatedCommand authenticatedCommand) { authenticatedCommand.UserId = UserId; } await CommandDispatcher.DispatchAsync(command); } } |
At first, we need to create an interface which marks the given command as the one that requires user to be authenticated, and then we can implement a brand new DispatchAsync
1 2 3 4 5 6 7 8 |
[Authorize] [HttpPost] public async Task<IActionResult> Post([FromBody]CreateDriver command) { await DispatchAsync(command); return Created($"drivers/{command.UserId}", null); } |
Next
In the next episode, we will write more and more business logic and also create a custom middleware for handling the exceptions.
Pingback: Dew Drop - May 18, 2017 (#2482) - Morning Dew
Hi
Is there an English version of those videos. I wish I understand your language but I’m sure the material is great.
Thanks
Unfortunately there is not.
May be you can add subtitle 🙁
Apologies, not possible for now.
After you buy Norton Antivirus visit norton.com/setup, sign in to norton account then enter norton product for Norton Setup or Install Norton … http://www.norton.com/setup. Protect your Pc/laptop and other devices with best Norton.com/setup Antivirus.