A model SOA application in .NET Part 5: the service layer continued

Introduction

In the previous post we started implementing the IProductService interface. We got as far as declaring a couple of private fields and a constructor. Here we’ll implement the ReserveProduct and PurchaseProduct methods.

The concrete service continued

Open the project we’ve been working on in this series and locate ProductService.cs. The implemented ReserveProduct method looks as follows:

public ProductReservationResponse ReserveProduct(ReserveProductRequest productReservationRequest)
{
	ProductReservationResponse reserveProductResponse = new ProductReservationResponse();
	try
	{
		Product product = _productRepository.FindBy(Guid.Parse(productReservationRequest.ProductId));
		if (product != null)
		{
			ProductReservation productReservation = null;
			if (product.CanReserveProduct(productReservationRequest.ProductQuantity))
			{
				productReservation = product.Reserve(productReservationRequest.ProductQuantity);
				_productRepository.Save(product);
				reserveProductResponse.ProductId = productReservation.Product.ID.ToString();
				reserveProductResponse.Expiration = productReservation.Expiry;
				reserveProductResponse.ProductName = productReservation.Product.Name;
				reserveProductResponse.ProductQuantity = productReservation.Quantity;
				reserveProductResponse.ReservationId = productReservation.Id.ToString();
			}
			else
			{
				int availableAllocation = product.Available();
				reserveProductResponse.Exception = new LimitedAvailabilityException(string.Concat("There are only ", availableAllocation, " pieces of this product left."));
			}
		}
		else
		{
			throw new ResourceNotFoundException(string.Concat("No product with id ", productReservationRequest.ProductId, ", was found."));
		}
	}
	catch (Exception ex)
	{
		reserveProductResponse.Exception = ex;
	}
	return reserveProductResponse;
}

First we let the product repository locate the requested product for us. If it doesn’t exist then we throw an exception with an appropriate message. We check using the CanReserveProduct method whether there are enough products left. If not then we let the client know in an exception message. Otherwise we reserve the product, save the current reservation status and populate the reservation response using the product reservation returned by the Save method. We wrap the entire code in a try-catch block to make sure that we catch any exception thrown during the process.

Here’s the implemented PurchaseProduct method:

public PurchaseProductResponse PurchaseProduct(PurchaseProductRequest productPurchaseRequest)
{
	PurchaseProductResponse purchaseProductResponse = new PurchaseProductResponse();
	try
	{
		if (_messageRepository.IsUniqueRequest(productPurchaseRequest.CorrelationId))
		{					
			Product product = _productRepository.FindBy(Guid.Parse(productPurchaseRequest.ProductId));
			if (product != null)
			{
				ProductPurchase productPurchase = null;
				if (product.ReservationIdValid(Guid.Parse(productPurchaseRequest.ReservationId)))
				{
					productPurchase = product.ConfirmPurchaseWith(Guid.Parse(productPurchaseRequest.ReservationId));
					_productRepository.Save(product);
					purchaseProductResponse.ProductId = productPurchase.Product.ID.ToString();
					purchaseProductResponse.PurchaseId = productPurchase.Id.ToString();
					purchaseProductResponse.ProductQuantity = productPurchase.ProductQuantity;
					purchaseProductResponse.ProductName = productPurchase.Product.Name;
				}
				else
				{
					throw new ResourceNotFoundException(string.Concat("Invalid or expired reservation id: ", productPurchaseRequest.ReservationId));
				}
				_messageRepository.SaveResponse<PurchaseProductResponse>(productPurchaseRequest.CorrelationId, purchaseProductResponse);
			}
			else
			{
				throw new ResourceNotFoundException(string.Concat("No product with id ", productPurchaseRequest.ProductId, ", was found."));
			}
		}
		else
		{
			purchaseProductResponse = _messageRepository.RetrieveResponseFor<PurchaseProductResponse>(productPurchaseRequest.CorrelationId);
		}
	}
	catch (Exception ex)
	{
		purchaseProductResponse.Exception = ex;
	}
	return purchaseProductResponse;
}

If you recall from the first part of this series we talked about the Idempotent pattern. The IsUniqueRequest is an application of the pattern. We check in the message repository if a message with that correlation ID has been processed before. If yes, then we return the response stored in the repository to avoid making the same purchase again. This is not the only possible solution of the pattern, but only one of the viable ones. Probably the domain layer could have a similar logic as well, but I think this is more robust.

If this is a new request then we locate the product just like in the ReserveProduct method and throw an exception if the product is not found. If the product exists then we need to check if the reservation can be made using the reservation ID of the incoming message. If not, then the reservation either doesn’t exist or has expired and a corresponding exception is thrown. Otherwise we confirm the purchase, save the product and dress up the product purchase response using the product purchase object returned by the ConfirmPurchaseWith method. Just like above, we wrap the code within a try-catch block.

This completes the service layer of the SOA project. We’ll look at the web client in the next post. The web client will be a web service client based on the Web API technology so that we don’t need to waste time and energy on presentation stuff such as HTML and CSS.

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.

6 Responses to A model SOA application in .NET Part 5: the service layer continued

  1. Kritner says:

    Really cool stuff. I’m hoping to get into an SOA approach in the near future and was really confused as to how to get started. This definitely helps with the creation of the services.

    Something that I have yet to understand is – say you were to create an authentication service which all other services called in order to either authenticate a user login, or ensure that the user is logged in – how does this service get integrated into other services without being a part of those projects and/or the authentication service being tightly coupled with all other services?

    I guess I get the idea that the services that call the authentication service have to know how to structure their calls to the authentication service, but this seems pretty tightly coupled at least in the way I’m thinking about it.

    Looking forward to your next post!

    • Andras Nemes says:

      Authentication is always a different beast. There are certainly many ways to integrate authentication and authorisation into a service and they all have their pros and cons. You can separate out this entirely by taking a single sign-on approach where the auth server and logic resides in an assembly separated from your main application.

      If you are then designing a SOA service like the ticketing service in this series then all users coming wishing to use it must authenticate with the auth server first and come to the ticketing service with a security token. The ticketing service must then evaluate the token early on in the application’s lifetime and reject the request with some appropriate error message if the token is invalid.

      The auth token can be generated in a couple of different ways. I have devoted a number of posts to Claims based security that can get you started.

      In mobile applications nowadays a different approach is taken: OAuth2, where login is delegated to providers such as Twitter, Google etc. You’ve probably seen consent screens such as “this application will be able to read your tweets”. That’s a telling sign of OAuth2 in the background.

      //Andras

  2. Allen Crane says:

    Hey Andras, Thanks a lot for posting this. I am digging into SOA architecture and it really helps. I just got your project from GITHUB and it looks like the SOATester project is missing. Can you hook a brother up?

    Thanks and again awesome work!

  3. Pingback: Architecture and patterns | Michael's Excerpts

  4. Pingback: Architecture and patterns | Michael's Excerpts

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 )

Twitter picture

You are commenting using your Twitter 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: