Software development practices and software testing approaches are different in each module for .NET Core applications. You cannot run tests on an ML.NET project the way you might test a Xamarin.Forms mobile application. A test written for a web application is different in terms of testing strategies as well as the libraries used. Thus, keeping everything in a single archive or package makes the code base and repository bloated with scripts. Most of the scripts that are generated for a web-based application’s test are run less than half the time. In your personal hobby application, you might change the front-end design, color, or typography every week to keep the content fresh. But you will change the back-end of your web app as needed and only when you are working on bug fixes or feature improvements. In a typical enterprise application, this would be different as they need to work on feature improvements, performance, and security related issues every day. This increases the execution of test suites on the web application, but it might decease this in a different application.
There is a need of separation of concerns here. Your front-end teams do not need to worry about the back-end issues. Similarly, a Xamarin.
Forms designer team does not need to worry about the web API issues that your cloud-native team is working on. When your teams work on a single code base for a complete solution, you are creating unavoidable issues for these teams. Your teams need to work on the code base and ensure that it works for everyone. Your back-end team might be releasing software upgrades every week, and your front-end team might be releasing software every two-four days. This adds burden to your back-end team to ensure the
code does not break for the front-end teams. Figure 3-4 shows a monolith scenario where if one thread crashes, it will crash the entire stack.
As seen in Figure 3-4, a monolith application contains the code for every aspect of the solution. From the business logic, to logging and views.
If there is a crash in any one of the components, the entire application crashes and impacts all the active users. Introduction of different concepts and domains like cloud-native Web APIs and services attempt to solve this problem.
This is where good old software engineering principles come into action. You explore the service-oriented architecture for solution development and deployment. A service-oriented architecture enforces some principles that let your teams develop software and services. Each service communicates with other services to provide a solution. Your solution is responsible for the communication between services and to Figure 3-4. In a typical application, users connect directly to a monolith application, sometimes deployed behind a load balancer, and that application is completely responsible for and contains the entire solution
provide a response to your customers. Your ASP.NET Core applications, for example, will—on average—contain the following:
• MVC controllers
• Web API controllers
• Views, or React, Angular, and Vue based front-end libraries
• Models and data layers: DbContexts in Entity Framework Core
• Logging services
• Cache and session stores
• Static files for CSS, JavaScript, and other media
You can easily distribute these files across multiple projects and manage them accordingly. Load balancers are more optimized to work on the static files; thus, you can extract the files in a separate project and run them behind a load balancer or proxy server. Nginx is a valid candidate in this category. Similarly, you should plan to separate the controllers from the views and data.
You can then separate each component into its own project, as a service. Since these services are a subsection in the complete solution, they can be worked individually to improve the solutions. Figure 3-5 shows a microservice approach of development; your solutions become independent programs and they run separately in their own processes.
A crash would not impact the entire stack and the solution will be online.
The microservices term is applied to this architecture in which each service is developed, tested, deployed, managed, and improved separately. This decreases the overall complexity of a project and enables the developers to focus on the project itself and the improvement of an individual service. Every service is developed separately, which removes the CI/CD burden of every other service. Each service has a separate lifecycle and a CI/CD pipeline, disconnected from the other CI/CDs.
Modifying the code of one service will not impact the other services.
As shown in Figure 3-5, separation of concerns enables the developers to work on products separately. Each service can communicate with a different service (or all services hosted). If a single service is down, your entire cluster does not crash. Your customers can continue to use the application if the logging component crashes. Logging components and their status will be updated on the operations team’s dashboard and they can fix the problems.
Figure 3-5. Different services can be deployed separately as microservices and the complete solution can be broken down as each microservice. In this configuration a load balancer is necessary, because your customers will not know the hostname or the port that a service would be listening on
I drew the diagram in Figure 3-5 with one point in mind—it should explain the chaos that enters the architecture. If you compare the figure of a monolith to a microservice, you can easily find the monolith to be simpler and better designed. In the case of microservices, chaos is added as a by-product. Microservices offer greater control over scalability and performance.
N-Tier Products with Hidden Databases
If you design an application using legacy architectures, you can still secure your application and improve performance. Regardless of your hosting and deployment platform, you can protect your production resources from external access. On cloud platforms like Microsoft Azure, AWS, or GCP, you can use internal virtual networks to restrict access to individual resources.