The course is part of this learning path
Store2008 Review - Overview of the .Net Monolithic Code
Store2018 - Refactor and Redesign
In this advanced course, we take a legacy monolithic .Net application and re-architect it to use a combination of cloud services to increase, scalability, performance, and manageability.
This course will enable you to:
- Learn the principles and patterns associated with microservices
- Understand the principles and patterns associated with Restful APIs
- Learn important requirements to consider when migrating a monolithic application into a microservices architecture
- Master the benefits of using microservices and associated software patterns and tools to build microservice based applications at speed and scale
- Understand tradeoffs between different architectural approaches
- Become comfortable with modern open source technologies such as Dotnet Core, Docker, Docker Compose, Linux, Terraform, Swagger, React
- Become familiar with Docker and Container orchestration runtimes to host and run containers, such as Docker Compose, Amazon ECS using Fargate, and Amazon EKS
- A basic understanding of software development
- A basic understanding of the software development life cycle
- A basic understanding of DevOps and CICD practices
- Familiarity with Dotnet and C#
- Familiarity with AWS
- Software Developers and Architects
- DevOps Practitioners interested in CI/CD implementation
- Anyone interested in understanding and adopting Microservices and Restful APIs within their own organization
- Anyone interested in modernizing an existing application
- Anyone interested in Docker, and Containers in general
- Anyone interested in container orchestration runtimes such as Kubernetes
- [Instructor] Welcome back. If you've been following along, you'll know that in our previous lecture we updated our service components, the account service, inventory service and shopping service. So each of these projects has now been web API enabled, meaning that external services can now integrate with these components using HTTP. So now in this lecture we're gonna focus on refactoring the Store2018 presentation layer component. So what we'll do is update the way that it interfaces with our services. So whereas previously in the monolithic application, this project imported each of these assemblies and then made in-process calls to each of the service functions.
The microservices equivalent approach will instead integrate with our services by making HTTP calls over the wire. So lets begin with the refactorings. So the first thing we'll do is we'll navigate into the controls folder and update the home controller. Now I've pasted in the code from our monolithic application just to highlight the differences that we're gonna change. So, there are expected errors here at the moment but we'll fix these. So the first thing we need to do is within our dependencies folder under projects, we now no longer need to maintain references to our services projects, as mentioned earlier. Instead our home controller will make HTTP calls directly to the web API interfaces exposed by each of these services. So the first thing we'll do is we'll remove each of the no longer required project references to our service components. So, removing each of these, cleans up that.
Next we can remove the using statements to those services as well. And while we're here we'll add in a couple of other, we'll import a couple of other classes. So we'll import Newtonsoft.Json. This will give us types to work with JSON data sets that will be returned from our service components. And secondly we'll add in System.Net.Http and this will give us an HTTP client to make calls to our service components. Okay, we now no longer need these static variables that in the monolithic application gave us access to our functions within our services. So we can delete that. And we'll clean out the index method and rebuild this. Okay, so we'll do a build and this should remove all the errors.
No, okay. We'll just return null. Fix that, and rebuild. Excellent. Okay the next thing that we need to do is take a copy of our models from each of the individual services. So we'll take a copy and we'll paste. That's for the account service. For the inventory service, copy the product model. Paste, and for the shopping service, copy both, and paste. Yep we're fine with that. And finally under models we need to add in our commerce model class. So we'll go general, empty class, we'll call it commerce, new. And then I'll just copy in the same code that was in our monolithic application. Okay so that's all our model types that will allow us to work with the data structures contained within each of these individual services. So before we recompile, we just need to make one final change to update the types that we're importing. So on each of the classes that we copied in, we need to update the namespace.
So we'll reset it to Store2018.Models. And finally on the product model. Okay so going back to our commerce model, fix this last bug. We need to import System.Collections.Generic. That will give us access to the list type. Excellent, so it looks like all the types are correctly imported. If we do a recompile, it should succeed, as it does. So the next thing we'll do is we'll go back to the home controller, and we'll begin to update the index method, which is used by the index view, which at the moment is rendering our home page. So the first thing we'll do is we'll add back in our commerce object, which tracks the logged on user, products that are purchasable, and the shopping cart items. So commerce is equal to new commerce. And if you remember, there are three properties on this. And for now, we'll just set them to null. And cart. Okay, so we now need to get the data for each of these, to set on these properties.
So how do we do that? Well remember we have web API enabled each of our services. So what we need to do is instantiate a HTTP client. And then we need to make calls to each of those web API endpoints. So we'll start by getting access to the user who's logged on. So var user equals new consumer. And we'll set up our HTTP request and response. So we'll need access to the HTTP response message. We'll call this response one, equals, we need to use the await word, we call the HTTP client, .GetAsync, and then the URL. Okay so to use the await operator, we need to make our method return an async type. So let's update our index method. So instead, this will become public async. And instead we'll return a task of type IActionResult. And then for the return type, we'll need to return a view, and in it we'll pass the commerce model that we're currently setting up. Okay, so that fixes up everything. We still need to do some more work. So we'll come back to the URL later on, but let's now put in some, let's check the status of the response. So we'll go response one .IsSuccessStatusCode. So if it is, we want to get access to the data.
So we'll go var result equals result one .Content.ReadAsStringAsync, and we want the result property. And then we'll extract the user by leveraging the JsonConvert.Deserialize method, DeserializeOject. And it's going to be deserialized as a consumer. And then we need to pass in the result. Okay so then we take the user that we've constructed by deserializing the JSON from calling the account service. And we need to set it on the user property of our commerce model, so, Okay, let's do a quick compile. Excellent, so that's all compiled. So we now need to repeat this. Before we do it, let's just add some documentation. So this represents the current consumer who's acting on the website. Okay so we now need to repeat this code block again, once for the other two services.
So in this case, we're going to make calls to the inventory service to get the list of products. So this represents the possible list of products that can be purchased. Okay so we'll create a products object, and I'll instantiate it as a list of product. We'll create a new response object, response two, and again we'll check the status code on it. And then again read the result, deserialize it, but this time, it needs to be deserialized as a list of product, and again we need to pass it the result to do the deserialization. And we need to change this to products.
Okay, so this code block here is making our web API call to the inventory service. Again we'll come back and put in the URL. So then finally, we again copy the same code block, and this time we're going to use it to represent the current consumer's selected shopping cart items. Okay, so we'll change this to cart, and it needs to be instantiated as a cart. And we'll make this response three. And we check the status code. We read the data, and then we set the cart object, we deserialize the result into the cart object. So this becomes of type cart. And then one last thing is we have to update our properties on our commerce model that we pass into our view. So this becomes products, and this becomes cart.
Okay, so we're looking pretty good. We have to come back and update the URLs, but for now let's just do a quick compile. And from a syntax point of view, everything is good. Okay, okay let's go back now and address the URLs, which are the endpoints to each of our services when we go to call them at runtime. So rather than hard coding the full URL, what we'll do is we'll use an environment variable to pass in the base of the URL. And then we'll use string interpolation to set up the full URL. So at the top of the class, let's set up a private static variable of type string, and we'll call it ACCOUNT_SERVICE_API_BASE. And we'll set it to Environment.GetEnvironmentVariable, and we'll look for an environment variable named the same name as the static variable. So we'll just copy that in there. Okay we'll repeat this three times, once for each of the API endpoints. So a second endpoint will be called inventory, and a third one will be called shopping. And we'll copy that to there and this to there.
Okay, so we've got one for each of our services. Inside the constructor of this class, we'll just do some runtime checking on those variables. So if the ACCOUNT_SERVICE_API_BASE is null, then we will hard code it to be local host. Port 5001/api. And again, we need to repeat this for the other environment variables. And this port will be 5002. And this port will be 5003. Okay, so that's our runtime checking. And finally, we need to pass in and construct our full URL. So we're gonna use string interpolation here. So the syntax for that is dollar sign, and then we, curly brackets, the variable that we're passing in, and on this we complete the path to consumers. In this case, we'll hard code it to the consumer represented by the ID number five. Okay, so let's repeat this for the inventory or the products. So in this case this becomes inventory, and it will be /products to get the full product list. And then finally we do the shopping cart API URL, so, and completing the path to cart/ and we'll hard code it to 30.
Okay, so we'll save that, and we'll do another compile. And everything's looking good. So now we're at a stage where we can test the full system. So we've completed all our refactorings. Let's give it an end-to-end test. But before we do, a couple of extra things. I've copied in the exact same views from the monolithic application without any changes. So they remain identical, as they were in the monolithic application. The second thing is our base URLs are using HTTP. So before we fire this up, we need to go back to each of our service projects and update the starting URL. So under default, ASP.NET core, we'll quickly change this to HTTP. So we're only doing this for debugging purposes. Again, changing it to HTTP. And then we do it for the last service. Okay, so now we'll scroll back down and we'll add in a couple of break points to look at the responses. So we'll break on each of the responses for the individual API calls. And we're ready to go. So let's start up the account service. So we'll select run item.
Okay that's running, and we'll do the same for inventory service. Excellent, and then once more for shopping service. Okay so our three services are up and running. And finally, we'll start up the presentation component. So if all goes we'll, we should hit our break points, and we'll be able to see our API calls. Okay here's the page loading, and excellent. So we've hit the first break point. So if we hover over, we should be able to see that indeed we're getting a 200, and if we step down, let me take a look at the result, we can see the JSON that's come back from the API call.
And if we step over once more, step out, we can see that our user has indeed been deserialized, and the object itself has been created. So Jeremy Cook, age 20, ID 5. So that's great. So let's continue, and we'll just run down to the third one. And if we put a break point on the view itself, we can see that the user property has been set.
The products property contains our products, and finally the cart contains the shopping cart items. Okay, click play finally, and we should see the complete view rendered in the browser, which we do. So in summary, what have we achieved? So we've completed our refactorings, and we've taking our project structure from monolithic to a microservices based architecture. And again, the advantages in doing so, it allows us to open up the business functions, encapsulated in each of these individual services, to other external services. So that now completes this lecture. Go ahead and close it, and we'll see you shortly in the next one, where we'll take a look at Docker as a method for packaging and running our microservices architecture using containers.
About the Author
Jeremy is the DevOps Content Lead at Cloud Academy where he specializes in developing technical training documentation for DevOps.
He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 20+ years. In recent times, Jeremy has been focused on DevOps, Cloud, Security, and Machine Learning.
Jeremy holds professional certifications for both the AWS and GCP cloud platforms.