Think with Enlab

Diving deep into the ocean of technology

Stay Connected. No spam!
  • Home
  • Development
  • How to build and deploy a 3-tier architecture application with C#

How to build and deploy a 3-tier architecture application with C#

 

Hi, back to the architecture patterns, in the last article I explained what the 3-layer architecture is and how to apply it to a project. Today we will continue to explore the 3-tier architecture which can be considered as a distributed system architecture. 

Good products are often built with a multi-tier architecture, a.k.a n-tiers. The most popular form of n-tier is the 3-tier application. Although the 3-tier application is a very well-known and prevalent architecture, it's not entirely apparent to developers who are new in software project development. This article will explain each tier of this architecture and share the best practices in our projects with C#.

What is 3-tier architecture?

A 3-tier architecture is a client-server architecture. The application is separated into physical computing tiers, which means the business logic, data storage, data access, and user interface are developed and maintained as single modules on separate platforms.

Similarly to the 3-layer architecture, the 3-tier architecture classifies an application into three main logical components but deploy them on different physical computing tiers:

  • Presentation tier
    The tier is the user interface of the application, where users interact with the application. Its main purpose is to display information to and collect data from users. This is the top-most level in the architecture and can be run on the web browser or a desktop/mobile application. 
  • Application tier
    This is the heart of the application, better known as the logic tier or middle tier. It coordinates all business logic of the application, prescribes how business objects interact with each other. It handles collected information from the Presentation tier. During the process, this tier may need to access the Data-tier to retrieve or modify the data. In a 3-tier application, all communication goes smoothly through the Application tier. The Presentation tier and the Data-tier can not communicate directly with one another.
  • Data-tier
    The Data-tier is sometimes referred to as the database tier, where it stores and manages the data processed by the Application tier. 

What are the differences between layer and tier?

There are divergent opinions on this issue. Layer and tier are often used interchangeably - this is a mistake.

The critical difference between layer and tier is about how it is organized. A multi-layer architecture refers to separating the application into multiple logical units that run on the same infrastructure. In contrast, a multi-tier architecture refers to separating the application into numerous physical units that run on separated infrastructures.

Another difference comes from the responsibility of the lowest component in each architecture:

  • Data-tier in 3-tier architecture: stores and retrieves data.
  • Data Access layer in 3-layer architecture: enforces rules regarding accessing data, providing simplified access to data stored in persistent storage.

The Data Access layer does not provide data, so in most cases, it will be in the Application tier (some designs separate it into a tier).

From that point of view, a tier can contain multiple layers. For example, a camera application on your phone is n-layer; it also can be called a single-tier application as all of the processes are executed on your phone:

  • Presentation layer: a user interacts with the app to capture an image
  • Business Logic layer: the app handles the captured image by converting it to binary, then sends the handled information to the Data Access layer.
  • Data Access layer: the app accesses the memory on your device to store the handled information.

Why is 3-tier architecture the most common use?

Because of the logical and physical separation of functionality, each tier can run on a separate hosting environment. Typical servers to deploy a 3-tier application are web server, application server, and database server, each serves appropriate functional requirements. Each tier operates separately, so its services can be customized and optimized without affecting the other tiers.

 

  • Speed up the development: As each tier can be enhanced simultaneously by different teams, so time to market of the product is optimized, and developers can use the latest tools and the best languages for each tier.
  • Improve the scalability: By deploying the application on different tiers, you are able to scale any tier independently of the others at any given time.
  • Improve the reliability: Because it has different tiers, you can also boost reliability and availability by running disparate parts of your application on distinct servers and employing cached results.
  • Improve the security: By using a well-designed application tier, it functions as a sort of internal firewall, which will help to prevent SQL injections and other malicious exploits.

How does it work?

ThreeTierArchitecture

The Presentation tier - the individual level where the user can interact with the application, collect data from the user, validate them if needed and then send it to the Application tier via an HTTP request.

The Application tier receives the (validated) data from the Presentation tier, then processes it by relevant business logic based on the Presentation requested. During the process, the Application tier may access the Data-tier to query data (retrieve/modify/store data) if necessary.

The Data-tier receives commands from the Application-tier, executes them, then sends a response back to the Application tier.

The Application tier receives the executed result and processes it. Once the process is completed, the Application tier sends the data back processed to the Presentation tier.

The Presentation tier receives the processed data and then presents them to the user.

How to build and deploy a 3-tier application with C#?

To guide you build and deploy a 3-tier application, I have prepared a demo with the following components:

Presentation Tier: Angular

Presentation tier

Application tier: Applying the 3-layer architecture for the Application tier. It is explained in How to build and deploy a 3-layer architecture application with C#.

Application tier

Data-tier: MS SQL Server

Data tier

To easily follow the article, you can download the demo of the 3-tier architecture sample.

The diagram below explains how the application was designed.

ThreeTierAppDesign

Data-tier with SQL Server

Create database project
Let’s use the database project template in Visual Studio 2019 and create a database project.

New projects

Design database
Once the project is created, design your tables, views, and stored procedures for this database. Also, add a script to initialize the data when the project is published to SQL Server.

Design database

Design Database 1

Design database 2

Design Database 3

Deployment
Right-click on the project and select Publish.

Deployment

Next, specify your SQL Server connection and database name, then click on Publish.

Database setting

 

Testing
Open your SSMS to check for the result.

Testing 3-tier

Application tier with C#

This is the heart of the 3-tier architecture, which is also the most complicated and challenging tier of implementation, so we need a good design to manage and organize the code. That is why I used 3-layer architecture for this tier.

This tier does not interact with users; it will interact with the other tiers/applications. This means the Presentation layer (of Application tier) is not a User Interface; it is an Application Interface, more commonly known as Application Programming Interface (API).

Please follow How to build and deploy a 3-layer architecture application with C#, the Data Access layer (Infrastructure), Domain layer, and Business Logic layer (Service) can be reused from this article. To accomplish the Presentation layer (of Application tier), I will use ASP.NET Core Web API as an interface to interact with the Presentation tier (in 3-tier architecture).

Create WebAPI layer - Asp.Net Core Web API Application

Let’s follow this tutorial to create an ASP.NET Core Web API application.

Once the application is developed, create a ServiceCollectionExtensions class under the Extensions folder.

ApplicationTier.API/Extensions/ServiceCollectionExtensions.cs

  • AddDatabase: register the database instances
  • AddServices: register service instances
  • AddCORS: access from external applications to this application(via API layer)

 

using System;
using ApplicationTier.Domain.Interfaces;
using ApplicationTier.Domain.Interfaces.Services;
using ApplicationTier.Domain.Models;
using ApplicationTier.Infrastructure;
using ApplicationTier.Service;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
 
namespace ApplicationTier.API.Extensions
{
   	public static class ServiceCollectionExtensions
	{
    	/// <summary>
    	/// Add needed instances for database
    	/// </summary>
    	/// <param name="services"></param>
    	/// <returns></returns>
    	public static IServiceCollection AddDatabase(this IServiceCollection services)
    	{
        	// Configure DbContext with Scoped lifetime  
      	  services.AddDbContext<DemoContext>(options =>
            	{
                    options.UseSqlServer(AppSettings.ConnectionString,
                    	sqlOptions => sqlOptions.CommandTimeout(120));
                    options.UseLazyLoadingProxies();
            	}
        	);
 
            services.AddScoped<Func<DemoContext>>((provider) => () => provider.GetService<DemoContext>());
            services.AddScoped<DbFactory>();
        	services.AddScoped<IUnitOfWork, UnitOfWork>();
 
        	return services;
    	}
 
    	/// <summary>
    	/// Add instances of in-use services
    	/// </summary>
    	/// <param name="services"></param>
    	/// <returns></returns>
    	public static IServiceCollection AddServices(this IServiceCollection services)
    	{
        	return services.AddScoped<IWorkService, WorkService>();
    	}
 
    	/// <summary>
    	/// Add CORS policy to allow external accesses
    	/// </summary>
    	/// <param name="services"></param>
    	/// <returns></returns>
    	public static IServiceCollection AddCORS(this IServiceCollection services)
    	{
        	return // CORS
            	services.AddCors(options => {
           	     options.AddPolicy("CorsPolicy",
                    	builder => {
                            builder.WithOrigins(AppSettings.CORS)
                                .AllowAnyMethod()
                                .AllowAnyHeader()
                                .AllowCredentials();
                    	});
            	});
    	}
	}
}

Open your AppSettings.cs class in the Domain layer, then update the following codes.

ApplicationTier.Domain/Models/AppSettings.cs

namespace ApplicationTier.Domain.Models
{
	public class AppSettings
	{
    	public static string ConnectionString { get; private set; }
    	public static string[] CORS { get; private set; }
	}
}

Update some values to your appsettings.json file.

ApplicationTier.API/appsettings.js

  • ConnectionString: the connection string to the database on Data-tier.
  • CORS: the domain list of applications that you allow access to the application.
     
{
  "Logging": {
	"LogLevel": {
  	"Default": "Information",
  	"Microsoft": "Warning",
  	"Microsoft.Hosting.Lifetime": "Information"
	}
  },
  "AppSettings": {
	"ConnectionString": "Data Source=(local);Initial Catalog=DataTier.SqlServer;Persist Security Info=True;User ID=sa;Password=PASSWORD;MultipleActiveResultSets=True",
	"CORS": [ "http://localhost:4200" ]
  },
  "AllowedHosts": "*"
}

Open the Startup.cs file, then add the following codes.

ApplicationTier.API/StartUp.cs

  • StartUp(constructor): read data from appsettings.json, then store it in the AppSettings class.
  • ConfigureServices: register instances for DataContext, its Factory, UnitOfWork, WorkService, and CORS policy to the application (using extension methods in the ServiceCollectionExtensions class).
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using System;
using ApplicationTier.API.Extensions;
using ApplicationTier.Domain.Models;
 
namespace ApplicationTier.API
{
	public class Startup
	{
    	public Startup(IWebHostEnvironment env)
    	{
        	Configuration = InitConfiguration(env);
    	}
 
    	public IConfiguration Configuration { get; }
 
    	// This method gets called by the runtime. Use this method to add services to the container.
    	public void ConfigureServices(IServiceCollection services)
    	{
        	services
            	.AddDatabase()
            	.AddServices()
                .AddCORS();
 
        	services.AddControllers();
        	services.AddSwaggerGen(c =>
        	{
            	c.SwaggerDoc("v1", new OpenApiInfo { Title = "ApplicationTier.API", Version = "v1" });
        	});
    	}
 
   	 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    	public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    	{
        	            if (env.IsDevelopment())
        	{
                app.UseDeveloperExceptionPage();
            	// Move swagger out of this if block if use want to use it on production
            	app.UseSwagger();
            	app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApplicationTier.API v1"));
        	}
 
        	// Auto redirect to https
        	//app.UseHttpsRedirection();
             // Allow external access;
        	app.UseCors("CorsPolicy");
        	app.UseRouting();
 
        	app.UseAuthorization();
 
        	app.UseEndpoints(endpoints =>
        	{
            	endpoints.MapControllers();
        	});
    	}
 
    	private IConfiguration InitConfiguration(IWebHostEnvironment env)
    	{
        	// Config the app to read values from appsettings base on current environment value.
        	var configuration = new ConfigurationBuilder()
                .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
            	.AddJsonFile("appsettings.json", false, true)
            	.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true)
                .AddEnvironmentVariables().Build();
        	//
        	// Map AppSettings section in appsettings.json file value to AppSetting model
        	configuration.GetSection("AppSettings").Get<AppSettings>(options => options.BindNonPublicProperties = true);
        	return configuration;
    	}
	}
}

Create a WorkController, add the following code to inject the interface of WorkService into the controller, then use it to implement some basic APIs.

ApplicationTier.API/Controllers/WorkController.cs
 

using System.Collections.Generic;
using System.Threading.Tasks;
using ApplicationTier.Domain.Entities;
using ApplicationTier.Domain.Interfaces.Services;
using Microsoft.AspNetCore.Mvc;
 
namespace ApplicationTier.API.Controllers
{
	[Route("api/[controller]")]
	[ApiController]
	public class WorkController : ControllerBase
	{
    	private readonly IWorkService _workService;
    	public WorkController(IWorkService workService)
    	{
        	_workService = workService;
    	}
 
    	#region CRUD
 
    	[HttpGet]
    	public async Task<IList<Work>> GetAll()
    	{
        	return await _workService.GetAll();
    	}
 
    	[HttpPut]
    	public async Task Update(Work work)
    	{
        	await _workService.Update(work);
        }
 
    	[HttpGet("{id:int}")]
    	public async Task<Work> GetOne([FromRoute] int id)
    	{
        	return await _workService.GetOne(id);
    	}
 
    	[HttpPost]
    	public async Task Add(Work work)
    	{
        	await _workService.Add(work);
    	}
 
    	[HttpDelete("{id}")]
    	public async Task Delete([FromRoute] int id)
    	{
        	await _workService.Delete(id);
    	}
 
    	#endregion
	}
}

Testing
Run your application to test the API via Swagger UI (already integrated when creating ASP.NET Core API app).

 

Testing API

Or by a specific API endpoint.

3-tier application

Deployment
In the machine where you want to store the Application tier creates a folder to store the source code.

Notes:  Make sure this machine has installed .NET 5 Bundle.

 

Application tier 1

On the same machine, open your IIS to create a website as below.

 

Application tier 2

Open your solution in your Visual Studio, then follow these steps:

Right-click on ApplicationTier.API, select Publish.

Application tier API publish

Select “Folder”.

Application 4 publish

Enter “Folder location” following the path of the created source code folder.

 

Publish

Click to publish the project.

 

Publish the project

Testing
Notes:  Please test on a specific URL with the suffix “api/work” as the app in this example has been configured for this route only.

Application tier API

Presentation tier with Angular

Create an Angular application
Open your UI folder, then run this command to create a new Angular project. “enlab-software” is the project’s name; you can name whatever you want.

ng new enlab-software

 

Angular

After creating, some files should be added to your project as below, or you can download a hero sample via the Angular portal. Then extract the file to your UI folder.

Next, move cmd to the created project folder and install all UI packages by running “npm install” command. Please ensure you have installed Node.js before running this command.
Wait until all packages have been installed; you can see the following illustration.

Enlab software

Next, run “npm start” to launch the project.

Enlab software 3-tier application

Open your browser on http://localhost:4200; the result is shown below.

 

Enlab software demo

Modify the application
Now open your editor to modify the app (I am using Webstorm).

src/environments/environment.ts (localhost)
src/environments/environment.prod.ts (production)

Set URL of the deployed Application tier to baseUrl value.

Modify the application

src/app/app.component.ts

import {Component} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {environment} from '../environments/environment';
 
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'Enlab Sofware Demo';
  works: any;
 
  constructor(private httpClient: HttpClient) {
	this.httpClient.get(`${environment.baseUrl}/api/work`)
  	.subscribe(works => {
    	this.works = works;
  	});
  }
}

 src/app/app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <h1>
	Welcome to <a href="https://enlabsoftware.com" target="_blank">{{ title }}!</a>
  </h1>
</div>
<table>
  <tr>
	<th style="width: 50px; text-align: left">Id</th>
	<th style="width: 100px; text-align: left">Name</th>
  </tr>
  <tr *ngFor="let work of works">
	<td>
  	{{work.id}}
    	</td>
	<td>
  	{{work.name}}
	</td>
  </tr>
</table>

Testing
Save all codes, then double-check your page on the browser.

Enlab software demo 1

Deployment

Publish the site

Deployment 2

On the same machine, open IIS, then create a site to host the UI app. In this demo, I created the site on the same machine as the Application machine. Despite the separation of this tier, you can deploy it to any machine you want. I set the port to 1234.

Presentation tier Angular

Open your cmd at the Angular project folder, then run “npm run build.

Angular project

Afterward, open the “dist” folder inside the angular project, copy all files, and paste it to the AngularUI site’s source folder in IIS.

AngularUI site

Then reload the UI site. Oops. Something went wrong!

Config CORS policy

Enlab demo

This happened because we have not told you about the Application tier that it should accept requests from the Presentation tier. To solve this problem, please follow these steps:

Open the appsettings.json file in the source code folder of the Application tier, then add the domain URL of the Presentation tier to CORS parameter.

 

Presentation tier to CORS parameter

On the Application machine, open IIS and restart the Application site.

Application tier API 1

Finally, reload the Presentation site on the browser. The site works now.

 

Enlab demo 2

Conclusion

The 3 tier architecture is a well-designed architecture widely used in the software industry because of its scalability, reliability, and high security. It is suitable for building large applications, which handle heavy business logic, require high-load, high security, and availability, such as e-commerce websites, SaaS (Software-as-a-Service), online payment gateway, etc.

Well, that is all I want to share about 3-tier architecture. Hopefully, it is helpful to you if you are going to start building a 3-tier architecture application.

Thank you for reading, and happy coding!

 

Contact us

References

1. IBM Cloud Education, What is Three-Tier Architecture, https://www.ibm.com
2. Scott Hanselman, A reminder on "Three/Multi-Tier/Layer Architecture/Design" brought to you by my late night frustrations, https://www.hanselman.com
3. Multitier architecture, https://en.wikipedia.org

About the author

Uyen Luu

Uyen Luu

Uyen Luu is currently a full-stack developer at Enlab Software. He is fluent in C# and VB programming languages and specializes in ASP.NET, .NET Core, Angular, Devextreme UI, Telerik Web controls, Kendo UI, etc.  He also has helped clients to develop several software applications, which contributed to his working experience in IIS, SQL Server, Power BI, Azure Development. 

Up Next

How to create real-time chat applications using WebSocket APIs in API Gateway
March 26,2021 by Trong Pham
My experience developing real-time messaging applications leveraging AWS services inspires me to share with the...
How to build and deploy a 3-layer application with C#?
February 27,2021 by Uyen Luu
What’s 3-layer architecture? Layer indicates the logical separation of components. Layered architecture concentrates on...
Repository and Unit of Work design patterns in .NET core
February 03,2021 by Loc Nguyen
Hi, back to the topic Repository & Unit of Work pattern, last time I explained...
Top logging frameworks for .NET applications
January 19,2021 by Tan Nguyen
In software development, logging is an essential part that helps you to monitor the system....

Can we send you our next blog posts? Only the best stuffs.

Subscribe