Click here to Skip to main content
13,863,229 members
Click here to Skip to main content
Add your own
alternative version

Stats

12.1K views
45 downloads
19 bookmarked
Posted 13 Feb 2019
Licenced CPOL

Simple Transaction: Microservices Sample Architecture for .NET Core Application

, 13 Feb 2019
Rate this:
Please Sign up or sign in to vote.
Microservices sample architecture for .NET Core Application

.NET Core 2.2 Sample with C#.NET, EF and SQL Server

Introduction

This is a .NET Core sample application and an example of how to build and implement a microservices based back-end system for a simple automated banking feature like Balance, Deposit, Withdraw in ASP.NET Core Web API with C#.NET, Entity Framework and SQL Server.

Application Architecture

The sample application is built based on the microservices architecture. There are several advantages in building an application using Microservices architecture like Services can be developed, deployed and scaled independently.The below diagram shows the high level design of back-end architecture.

  • Identity Microservice - Authenticates user based on username, password and issues a JWT Bearer token which contains Claims-based identity information in it
  • Transaction Microservice - Handles account transactions like Get balance, deposit, withdraw
  • API Gateway - Acts as a center point of entry to the back-end application, Provides data aggregation and communication path to microservices.

Application Architecture

Design of Microservice

This diagram shows the internal design of the Transaction Microservice. The business logic and data logic related to transaction service is written in a separate transaction processing framework. The framework receives input via Web API and processes those requests based on some simple rules. The transaction data is stored up in SQL database.

Microservice design

Security: JWT Token based Authentication

JWT Token based authentication is implemented to secure the WebApi services. Identity Microservice acts as a Auth server and issues a valid token after validating the user credentials. The API Gateway sends the token to the client. The client app uses the token for the subsequent request.

JWT Token based Security

Development Environment

Technologies

  • C#.NET
  • ASP.NET WEB API Core
  • SQL Server

Opensource Tools Used

  • Automapper (for object-to-object mapping)
  • Entity Framework Core (for Data Access)
  • Swashbucke (for API documentation)
  • XUnit (for Unit test case)
  • Ocelot (for API Gateway Aggregation)

Cloud Platform Services

  • Azure App Insights (for Logging and Monitoring)
  • Azure SQL Database (for Data store)

Database Design

Database Design

WebApi Endpoints

The application has four API endpoints configured in the API Gateway to demonstrate the features with token based security options enabled. These routes are exposed to the client app to consume the back-end services.

End-Points Configured and Accessible Through API Gateway

  1. Route: "/user/authenticate" [HttpPost] - to authenticate user and issue a token
  2. Route: "/account/balance" [HttpGet] - to retrieve account balance
  3. Route: "/account/deposit" [HttpPost] - to deposit amount in an account
  4. Route: "/account/withdraw" [HttpPost] - to withdraw amount from an account

End-Points Implemented at the Microservice Level

  1. Route: "/api/user/authenticate" [HttpPost]- to authenticate user and issue a token
  2. Route: "/api/account/balance" [HttpGet]- to retrieve account balance
  3. Route: "/api/account/deposit" [HttpPost]- to deposit amount in an account
  4. Route: "/api/account/withdraw" [HttpPost]- to withdraw amount from an account

Solution Structure

Solution Structure

  • Identity.WebApi
    • Handles the authentication part using username, password as input parameter and issues a JWT Bearer token with Claims-Identity information in it.
  • Transaction.WebApi
    • Supports three http methods, 'Balance', 'Deposit' and 'Withdraw'. Receives http request for these methods.
    • Handles exception through a middleware
    • Reads Identity information from the Authorization Header which contains the Bearer token
    • Calls the appropriate function in the 'Transaction' framework
    • Returns the transaction response result back to the client
  • Transaction.Framework
    • Defines the interface for the repository (data) layer and service (business) layer
    • Defines the domain model (Business Objects) and Entity Model (Data Model)
    • Defines the business exceptions and domain model validation
    • Defines the required data types for the framework 'Struct', 'Enum', 'Constants'
    • Implements the business logic to perform the required account transactions
    • Implements the data logic to read and update the data from and to the SQL database
    • Performs the task of mapping the domain model to entity model and vice versa
    • Handles the db update concurrency conflict
    • Registers the Interfaces and its Implementation into Service Collection through dependency injection
  • Gateway.WebApi
    • Validates the incoming Http request by checking for authorized JWT token in it
    • Reroutes the Http request to a downstream service
  • SimpleBanking.ConsoleApp
    • A console client app that connects to Api Gateway, can be used to login with username, password and perform transactions like 'Balance', 'Deposit' and 'Withdraw' against an account.

Exception Handling

A Middleware is written to handle the exceptions and it is registered in the startup to run as part of http request. Every http request, passes through this exception handling middleware and then executes the Web API controller action method.

  • If the action method is successful, then the success response is sent back to the client.
  • If any exception is thrown by the action method, then the exception is caught and handled by the Middleware and appropriate response is sent back to the client.

Exception Handler Middleware

public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
    try
    {
        await next(context);
    }
    catch (Exception ex)
    {
        var message = CreateMessage(context, ex);
        _logger.LogError(message, ex);

        await HandleExceptionAsync(context, ex);
    }
}

Db Concurrency Handling

Db concurrency is related to a conflict when multiple transactions are trying to update the same data in the database at the same time. In the below diagram, if you see that Transaction 1 and Transaction 2 are against the same account, one trying to deposit amount into account and the other system trying to withdraw amount from the account at the same time. The framework contains two logical layers, one handles the Business Logic and the other handles the Data logic.

Db Concurrency Update Exception

When a data is read from the DB and when business logic is applied to the data, at this context, there will be three different states for the values relating to the same record.

  • Database values are the values currently stored in the database
  • Original values are the values that were originally retrieved from the database
  • Current values are the new values that application is attempting to write to the database

The state of the values in each of the transactions produces a conflict when the system attempts to save the changes and identifies using the concurrency token that the values being updated to the database are not the Original values that were read from the database and it throws DbUpdateConcurrencyException.

Reference: docs.microsoft.com

The general approach to handle the concurrency conflict is:

  1. Catch DbUpdateConcurrencyException during SaveChanges
  2. Use DbUpdateConcurrencyException.Entries to prepare a new set of changes for the affected entities
  3. Refresh the original values of the concurrency token to reflect the current values in the database
  4. Retry the process until no conflicts occur
while (!isSaved)
{
    try
    {
        await _dbContext.SaveChangesAsync();
        isSaved = true;
    }
    catch (DbUpdateConcurrencyException ex)
    {
        foreach (var entry in ex.Entries)
        {
            if (entry.Entity is AccountSummaryEntity)
            {
                var databaseValues = entry.GetDatabaseValues();

                if (databaseValues != null)
                {
                    entry.OriginalValues.SetValues(databaseValues);
                    CalculateNewBalance();

                    void CalculateNewBalance()
                    {
                        var balance = (decimal)entry.OriginalValues["Balance"];
                        var amount = accountTransactionEntity.Amount;

                        if (accountTransactionEntity.TransactionType == 
                                                   TransactionType.Deposit.ToString())
                        {
                            accountSummaryEntity.Balance =
                            balance += amount;
                        }
                        else if (accountTransactionEntity.TransactionType == 
                                                       TransactionType.Withdrawal.ToString())
                        {
                            if(amount > balance)
                                throw new InsufficientBalanceException();

                            accountSummaryEntity.Balance =
                            balance -= amount;
                        }
                    }
                }
                else
                {
                    throw new NotSupportedException();
                }
            }
        }
    }
}  

Azure AppInsights: Logging and Monitoring

Azure AppInsights integrated into the "Transaction Microservice" for collecting the application Telemetry.

public void ConfigureServices(IServiceCollection services)
{           
   services.AddApplicationInsightsTelemetry(Configuration);           
}

AppInsights SDK for ASP.NET Core provides an extension method AddApplicationInsights on ILoggerFactory to configure logging. All transactions related to Deposit and Withdraw are logged through ILogger into AppInsights logs.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory log)
{
   log.AddApplicationInsights(app.ApplicationServices, LogLevel.Information);     
}

To use AppInsights, you need to have an Azure account and create an AppInsights instance in the Azure Portal for your application, that will give you an instrumentation key which should be configured in the appsettings.json.

"ApplicationInsights": {
   "InstrumentationKey": "<Your Instrumentation Key>"
 },

Swagger: API Documentation

Swashbuckle Nuget package is added to the "Transaction Microservice" and Swagger Middleware configured in the startup.cs for API documentation. When running the WebApi service, the swagger UI can be accessed through the swagger endpoint "/swagger".

public void ConfigureServices(IServiceCollection services)
{            
     services.AddSwaggerGen(c => {
        c.SwaggerDoc("v1", new Info { Title = "Simple Transaction Processing", Version = "v1" });
     });
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory log)
{           
     app.UseSwagger();
     app.UseSwaggerUI(c => {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "Simple Transaction Processing v1");
     });           
}

Swagger API Doc

Postman Collection

Download the postman collection from here to run the API endpoints through gateway.

Postman

How to Run the Application

  1. Download the SQL script from here.
  2. Run the script against SQL server to create the necessary tables and sample data.
  3. Open the solution (.sln) in Visual Studio 2017 or later version.
  4. Configure the SQL connection string in Transaction.WebApi -> Appsettings.json file
  5. Configure the AppInsights Instrumentation Key in Transaction.WebApi -> Appsettings.json file. If you don't have a key or don't require logs, then comment the AppInsight related code in Startup.cs file.
  6. Check the Identity.WebApi -> UserService.cs file for Identity information. User details are hard coded for few accounts in Identity service which can be used to run the app. Same details are shown in the below table.
  7. Run the following projects in the solution:
    • Identity.WebApi
    • Transaction.WebApi
    • Gateway.WebApi
    • SimpleBanking.ConsoleApp
  8. Gateway host and port should be configured correctly in the ConsoleApp.
  9. Identity and Transaction service host and port should be configured correctly in the gateway -> configuration.json.
  • Sample data to test:
    Account Number Currency Username Password
    3628101 EUR speter test@123
    3637897 EUR gwoodhouse pass@123
    3648755 EUR jsmith admin@123

Console App - Gateway Client

Console App

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

John-ph
Architect
India India
Experienced Software Developer, Architect, Leader with a demonstrated history of working in the software industry. Skilled in Microsoft .Net technology, Cloud computing, Solution Design, Software Architecture, Enterprise integration, Service Oriented and Microservices based Application Development. Currently, focusing on .Net Core, Web API, Microservices, Azure

You may also be interested in...

Pro

Comments and Discussions

 
QuestionNice article. Pin
Bao Tran 198214-Feb-19 16:28
professionalBao Tran 198214-Feb-19 16:28 
AnswerRe: Nice article. Pin
John-ph14-Feb-19 19:41
memberJohn-ph14-Feb-19 19:41 
GeneralRe: Nice article. Pin
Bao Tran 198216-Feb-19 23:35
professionalBao Tran 198216-Feb-19 23:35 
QuestionGithub to evolve the application? Pin
Marcelo Mohr Maciel14-Feb-19 9:30
memberMarcelo Mohr Maciel14-Feb-19 9:30 
AnswerRe: Github to evolve the application? Pin
John-ph14-Feb-19 18:47
memberJohn-ph14-Feb-19 18:47 
QuestionPlease dont use automapper!! Pin
Paolo Vigori13-Feb-19 9:56
memberPaolo Vigori13-Feb-19 9:56 
QuestionRe: Please dont use automapper!! Pin
John-ph14-Feb-19 21:10
memberJohn-ph14-Feb-19 21:10 
AnswerRe: Please dont use automapper!! Pin
Paolo Vigori14-Feb-19 22:19
memberPaolo Vigori14-Feb-19 22:19 
QuestionUnable to download source code Pin
Member 1414191013-Feb-19 6:14
memberMember 1414191013-Feb-19 6:14 
AnswerRe: Unable to download source code Pin
John-ph13-Feb-19 7:11
memberJohn-ph13-Feb-19 7:11 
GeneralRe: Unable to download source code Pin
Member 1414191014-Feb-19 2:57
memberMember 1414191014-Feb-19 2:57 
QuestionDownload project link is not working. Pin
Ashwani KS13-Feb-19 3:33
memberAshwani KS13-Feb-19 3:33 
AnswerRe: Download project link is not working. Pin
John-ph13-Feb-19 7:11
memberJohn-ph13-Feb-19 7:11 
GeneralRe: Download project link is not working. Pin
Ashwani KS14-Feb-19 21:02
memberAshwani KS14-Feb-19 21:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04 | 2.8.190214.1 | Last Updated 13 Feb 2019
Article Copyright 2019 by John-ph
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid