DEV Community

Cover image for Getting Started with Azure Service Fabric for Local Development: A Real-World Banking Example
Treveshan Naidoo
Treveshan Naidoo

Posted on

Getting Started with Azure Service Fabric for Local Development: A Real-World Banking Example

Azure Service Fabric is a robust platform for building microservice-based applications, allowing for scalability, resilience, and easy management of distributed services. In this guide, I’ll walk you through getting started with Azure Service Fabric for local development, using a real-world banking example where services communicate with each other.

In this scenario, we’ll create two microservices:

  • AccountService: Manages customer accounts (checking balance, updating balance, etc.).
  • TransactionService: Handles deposits, withdrawals, and transfers, and communicates with the AccountService.

This example demonstrates how services talk to each other using Service Fabric Remoting.

Why Service Fabric?

Service Fabric is ideal for microservices that need:

  • Scalability: Handle more services as your app grows.
  • Resilience: Services automatically restart and recover from failures.
  • Service Communication: Allows microservices to easily communicate with each other.

Step 1: Setting Up the Development Environment

Before we jump into code, you’ll need to set up your development environment.

  1. Install Visual Studio 2022 or Later: Make sure you have the latest version, with .NET and Azure workloads enabled.
  2. Install Service Fabric SDK and Tools: You can download the Service Fabric SDK here.
  3. Start the Service Fabric Local Cluster Manager: After installation, start the local cluster manager. This will create a local Service Fabric cluster on your machine where your services will run.

Step 2: Creating a Service Fabric Application

Now let’s create our Service Fabric application in Visual Studio.

  1. Open Visual Studio and create a new project.
  2. Select Service Fabric Application and name it BankingApp.
  3. Choose Stateless Service for both the AccountService and TransactionService.

Your solution will contain the following structure:

  • Application Project: Manages service definitions and deployments.
  • Service Projects: Contains business logic for the services (AccountService and TransactionService).

Step 3: Defining the Service Interfaces

We need to define an interface to enable communication between the TransactionService and the AccountService. The TransactionService will call methods on the AccountService to check and update account balances.

AccountService Interface

using System.Threading.Tasks;
using Microsoft.ServiceFabric.Services.Remoting;

namespace AccountService
{
    public interface IAccountService : IService
    {
        Task<decimal> GetBalanceAsync(string accountId);
        Task<bool> UpdateBalanceAsync(string accountId, decimal amount);
    }
}
Enter fullscreen mode Exit fullscreen mode

This interface exposes two methods:

  • GetBalanceAsync: Retrieves the balance of a specific account.
  • UpdateBalanceAsync: Updates the balance of an account after a transaction (deposit/withdrawal).

Step 4: Implementing AccountService

Next, we implement the logic in AccountService. For simplicity, we will use an in-memory dictionary to store account data.

AccountService Implementation

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.ServiceFabric.Services.Remoting.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;

namespace AccountService
{
    internal sealed class AccountService : StatelessService, IAccountService
    {
        // Simulate a simple account database with account IDs and balances
        private readonly Dictionary<string, decimal> _accounts = new Dictionary<string, decimal>
        {
            { "A001", 1000 },  // Account ID A001 with an initial balance of 1000
            { "A002", 500 },   // Account ID A002 with an initial balance of 500
            { "A003", 750 }    // Account ID A003 with an initial balance of 750
        };

        public AccountService(StatelessServiceContext context) : base(context) { }

        public Task<decimal> GetBalanceAsync(string accountId)
        {
            if (_accounts.TryGetValue(accountId, out var balance))
            {
                return Task.FromResult(balance);
            }
            else
            {
                return Task.FromResult(0m); // Return 0 if the account doesn't exist
            }
        }

        public Task<bool> UpdateBalanceAsync(string accountId, decimal amount)
        {
            if (_accounts.ContainsKey(accountId))
            {
                _accounts[accountId] += amount;
                return Task.FromResult(true); // Successfully updated
            }

            return Task.FromResult(false); // Account doesn't exist
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Here, AccountService contains an in-memory dictionary to manage accounts and their balances. You can retrieve an account’s balance and update it based on deposits or withdrawals.

Step 5: Implementing TransactionService

Now, let’s implement the TransactionService, which will interact with the AccountService to perform transactions like deposits and withdrawals.

TransactionService Implementation

using System;
using System.Threading.Tasks;
using Microsoft.ServiceFabric.Services.Client;
using Microsoft.ServiceFabric.Services.Remoting.Client;
using Microsoft.ServiceFabric.Services.Runtime;

namespace TransactionService
{
    internal sealed class TransactionService : StatelessService
    {
        public TransactionService(StatelessServiceContext context) : base(context) { }

        public async Task<bool> PerformDepositAsync(string accountId, decimal amount)
        {
            var accountService = ServiceProxy.Create<IAccountService>(
                new Uri("fabric:/BankingApp/AccountService"),
                new ServicePartitionKey(0));

            // Get the current balance
            var balance = await accountService.GetBalanceAsync(accountId);

            if (balance >= 0)
            {
                // Update the balance
                await accountService.UpdateBalanceAsync(accountId, amount);
                ServiceEventSource.Current.ServiceMessage(this.Context, 
                    $"Deposited {amount} to account {accountId}. New balance: {balance + amount}");
                return true;
            }

            ServiceEventSource.Current.ServiceMessage(this.Context, 
                $"Failed to deposit to account {accountId}. Account not found.");
            return false;
        }

        public async Task<bool> PerformWithdrawalAsync(string accountId, decimal amount)
        {
            var accountService = ServiceProxy.Create<IAccountService>(
                new Uri("fabric:/BankingApp/AccountService"),
                new ServicePartitionKey(0));

            // Get the current balance
            var balance = await accountService.GetBalanceAsync(accountId);

            if (balance >= amount)
            {
                // Update the balance by deducting the amount
                await accountService.UpdateBalanceAsync(accountId, -amount);
                ServiceEventSource.Current.ServiceMessage(this.Context, 
                    $"Withdrew {amount} from account {accountId}. New balance: {balance - amount}");
                return true;
            }

            ServiceEventSource.Current.ServiceMessage(this.Context, 
                $"Insufficient funds for withdrawal from account {accountId}.");
            return false;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • TransactionService uses ServiceProxy to create a proxy for AccountService, allowing it to make remote calls like GetBalanceAsync and UpdateBalanceAsync.
  • The PerformDepositAsync method checks the balance, updates it, and logs the operation.
  • The PerformWithdrawalAsync method performs a balance check before deducting the amount.

Step 6: Running the Services Locally

You can now run both services locally on your machine.

  1. Start the Local Cluster: Open Service Fabric Local Cluster Manager and start the local cluster if it’s not already running.
  2. Deploy the Application: Press F5 in Visual Studio to build and deploy the application locally.
  3. Inspect Services: Open Service Fabric Explorer at http://localhost:19080/Explorer to see both services running. You can inspect their status and health.

Step 7: Testing the Services

To test the TransactionService, you can simulate deposit and withdrawal operations by calling its methods from an external API or through a unit test.

Example Test for Deposits:

var transactionService = new TransactionService();
await transactionService.PerformDepositAsync("A001", 500); // Deposit $500 into account A001
Enter fullscreen mode Exit fullscreen mode

Example Test for Withdrawals:

var transactionService = new TransactionService();
await transactionService.PerformWithdrawalAsync("A002", 200); // Withdraw $200 from account A002
Enter fullscreen mode Exit fullscreen mode

You should see the corresponding logs in the Visual Studio output window or in Service Fabric Explorer.

Real-World Banking Expansion

In a real-world banking system, additional services could be added to handle other operations:

  • LoanService: Manage customer loans and their repayment schedules.
  • NotificationService: Send real-time notifications to customers after transactions.
  • AuditService: Track and log all transactions for regulatory compliance.

These services could communicate asynchronously using Azure Service Bus or Event Grid to decouple them and ensure that failures in one service do not disrupt others.

Conclusion

In this article, we built a simple banking application using Azure Service Fabric, demonstrating how services like TransactionService and AccountService can communicate using Service Fabric Remoting. This setup forms the backbone of a scalable and resilient microservices architecture.

Ready to dive deeper into Azure Service Fabric? Explore advanced topics like stateful services, partitioning, and deploying to production clusters. Follow me for more in-depth articles about Service Fabric and microservices development!

Top comments (0)