Click here to Skip to main content
15,868,016 members
Articles / Hosted Services / Azure

CloudBank

Rate me:
Please Sign up or sign in to vote.
5.00/5 (8 votes)
8 Dec 2022CPOL4 min read 14K   106   14   2
Demo front end to show Blazor UI over Azure functions using Event Sourcing for the data storage
This is a client side web application that sends commands to the Bank Account event sourcing demo application which uses Azure Tables as the storage mechanism for the event streams underlying each bank account.

CloudBank is a demo front end to show how to put a Blazor UI front end over Azure functions using Event Sourcing for the data storage

This client side web application sends commands to the Bank Account event sourcing demo application which uses Azure Tables as the storage mechanism for the event streams underlying each bank account.

You can try it out from any modern web browser at this URL

Using the application

Cloudbank front screen

When the application is launched the front screen as above is shown, with the navigation bar down the left hand side. This is pretty much an out of the box standard Blazor site template.

Creating a new account

Click on the User menu to go to the form that allows you to create a new bank account. You can either type your own choice of bank account number of select the generate random account number button to have the system create one.

Create new bank account

Once you have created a new account you can navigate to it with the navigate to {accountnumber} button.

Getting the account balance

To get the account balance select the first section and it will open up and the button get balance can be used to trigger the Azure function that gets the balance for this bank account.

Get account balance

Because the backing store is event sourced it is just as easy to get the state as at some past date as it is to get the current state so you can enter an as of date and the system will return the account balance as at that date.

Account balance retrieved

Making a deposit

The second expandable section allows you to make a deposit.

Make a deposit

There are parameters to fill in for the deposit amount, deposit source and any commentary and then pressing the submit button will pass these to the Azure function to make the deposit.

Deposit made

When a the deposit is made a message will indicate this.

Making a withdrawal

The third expandable section is for making a withdrawal.

Make withdrawal

This has parameters for ammount and for commentary and again pressing the submit button sends these parameters to the Azure function.

Made Withdrawal

Because the fuunction to make a withdrawal needs to run a projection that checks the current balance (to prevent the account being overdrawn beyond its limit) this function also returns information about the account balance.

Setting an overdraft

The lowest of the expandable section allows for an overdraft to be set.

Setting an overdraft

Pressing the submit button having filled in all the parameters will pass these to the Azure function to execute them.

Overdraft set

Again, because this Azure function has to get the existing overdraft and balance for its validation it can return this information to the front end.

For every Azure function executed the system will also return the total time it took to execute the function and if the function also runs any projections over the event stream it will also return the sequence number of the last event read.

How it works

The back end of this application is running as Azure serverless functions which are backed by event sourcing with the event streams for each account held in an Azure table. This is described in this GitHub project

On the front end the Razor pages inject the standard http client and a RetailBankApi class which is used for all comminication with the Azure functions:

@using CloudBank.Services


@page <span class="pl-pds">"/myaccount"</span>
@page <span class="pl-pds">"/myaccount/{accountnumber}"</span>

@inject HttpClient Http
@inject IRetailBankApi retailBankApi

This RetailBankApi class is injected into every page by use of dependency injection in the Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    <span class="pl-c">// Modal forms</span>
    services.AddBlazoredModal();

    <span class="pl-c">// Bank account API</span>
    services.AddScoped <IRetailBankApi, RetailBankApiNoHttp>();
}

Behind the scenes the RetailBankApiNoHttp class loads the set of Azure functions from a configuration file called bank-api.json (which allows the application to have its back end changed without impacting the deployed front end) - this is loaded in the MainLayout.razor file:

@code
{

    [Parameter]
    public ApiCommand[] apiCommands { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (null == apiCommands)
        {
            apiCommands = await Http.GetJsonAsync<ApiCommand[]>(<span class="pl-pds">"sample-data/bank-api.json"</span>);
            if (null != apiCommands)
            {
                <span class="pl-c">// fix up the rertail bank api with them...</span>
                retailBankApi.Initialise(apiCommands);
            }
        }
    }
}

To call these from the account actions razore page we have action code wired up for the submit button like:

<button @onclick=<span class="pl-pds">"@(() => GetBalance())"</span> class=<span class="pl-pds">"btn btn-secondary"</span>>
       <span class=<span class="pl-pds">"oi oi-cloud-download"</span> aria-hidden=<span class="pl-pds">"true"</span>></span>
        Get Balance
 </button>

When triggered this calls the Azure function thus:

private async Task GetBalance()
{
    LastFunctionMessage = <span class="pl-pds">$"Getting balance for {accountnumber} at {getbalancePayload.AsOfDate}  "</span>;

    try
    {
        var result = await retailBankApi.GetAccountBalance(Http, accountnumber, getbalancePayload);
        LastFunctionMessage = result.Message;
        LastRunTime = result.ExecutionTime;
        LastSequenceNumber = result.SequenceNumber;
    }
    catch (Exception ex)
    {
        LastFunctionMessage = ex.Message;
    }

}

This sends the command to the Azure function by http:

<span class="pl-c">/// <<span class="pl-ent">summary</span>></span>
<span class="pl-c">/// Get the current balance of an account </span>
<span class="pl-c">/// </<span class="pl-ent">summary</span>></span>
public async Task<ProjectionFunctionResponse> GetAccountBalance(HttpClient httpclient, string accountnumber,  GetBalanceData payload)
{
    if (null != httpclient)
    {

        string key = <span class="pl-pds">""</span>;
        key = _commands.FirstOrDefault(f => f.CommandName == <span class="pl-pds">"Get Balance"</span>)?.ApiKey;

        HttpRequestMessage message = new HttpRequestMessage()
        {
            Method = HttpMethod.Get,
            RequestUri = new Uri(RetailBankApi.GetBase(), RetailBankApi.GetBalance(accountnumber, payload.AsOfDate , key))
        };


        var response = await httpclient.SendAsync(message);
        if (response.IsSuccessStatusCode)
        {
            var jsonString= await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<ProjectionFunctionResponse>(jsonString);
        }
        else
        {
            return new ProjectionFunctionResponse() { Message = <span class="pl-pds">$"Unable to get balance - {response.StatusCode}"</span>, InError = true };
        }
    }

    return new ProjectionFunctionResponse() { Message = <span class="pl-pds">$"Not connected to retail bank API"</span>, InError = true };
}

Deployment

Because this Blazor front end runs entirely on the client it can be hosted on a static website - and the static website functionality that you get with an Azure storage account is perfect for this.

Azure storage static website

You will need to add this static website URL to the CORS settings of your Azure functions application so that it is allowed to be accessed from there:

Azure functions CORS settings

This article was originally posted at https://github.com/MerrionComputing/CloudBank

License

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


Written By
Software Developer
Ireland Ireland
C# / SQL Server developer
Microsoft MVP (Azure) 2017
Microsoft MVP (Visual Basic) 2006, 2007

Comments and Discussions

 
QuestionDownload links are broken Pin
Franck Quintana17-Dec-20 21:54
Franck Quintana17-Dec-20 21:54 
AnswerRe: Download links are broken Pin
Duncan Edwards Jones9-Jan-21 0:50
professionalDuncan Edwards Jones9-Jan-21 0:50 

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.