DEV Community

Cover image for Understanding the Proxy Pattern with a Payroll System Example
Daniel Azevedo
Daniel Azevedo

Posted on

Understanding the Proxy Pattern with a Payroll System Example

Hi devs,

The Proxy Pattern is a common design pattern that many developers might use without even realizing it. It’s part of the Structural Design Patterns family and is used when you want to provide a surrogate or placeholder to control access to another object.

In simpler terms, the proxy pattern allows you to create an intermediary that controls access to a resource, such as an object that is expensive to create or requires security checks.

Let's explore this pattern through the lens of a payroll system. Imagine a scenario where calculating employee salaries requires fetching data from a remote service, and this process is both time-consuming and resource-intensive.


What is the Proxy Pattern?

The Proxy Pattern involves creating a proxy (intermediary) that represents another object, controlling access to it. This intermediary can help manage object creation, security, logging, and even caching.

Some common types of proxies:

  • Virtual Proxy: Used to delay the creation of expensive objects until they are actually needed.
  • Protection Proxy: Controls access to sensitive data, enforcing rules and permissions.
  • Remote Proxy: Represents an object that exists in a remote location, like a server or API.

When to Use the Proxy Pattern?

In a payroll system, there could be several scenarios where the proxy pattern can be applied:

  • Lazy Loading: Only calculate the payroll when required.
  • Access Control: Restrict access to sensitive payroll data, only allowing authorized users to view or modify salary details.
  • Logging and Auditing: Automatically log every time sensitive salary data is accessed or modified.

Example: Virtual Proxy in a Payroll System

Let’s say we have a payroll system where we want to calculate employee salaries. Fetching this data is expensive because it requires contacting an external API. We don’t want to fetch the data until it’s absolutely necessary.

Here’s how we can apply the Proxy Pattern to solve this issue:

Step 1: Define the Interface

// Payroll interface that defines the method to get employee salary
public interface IPayroll
{
    decimal GetSalary();
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Implement the Real Payroll Calculation

// Real Payroll class that does the actual salary calculation
public class RealPayroll : IPayroll
{
    private string _employeeId;

    public RealPayroll(string employeeId)
    {
        _employeeId = employeeId;
        // Simulate expensive operation (e.g., fetching salary details from an API)
        Console.WriteLine("Fetching salary details from the payroll system...");
    }

    public decimal GetSalary()
    {
        // Simulate the salary calculation for the employee
        return 5000.00M; // Example salary
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Implement the Proxy

// Proxy class to control access to RealPayroll
public class PayrollProxy : IPayroll
{
    private RealPayroll _realPayroll;
    private string _employeeId;

    public PayrollProxy(string employeeId)
    {
        _employeeId = employeeId;
    }

    public decimal GetSalary()
    {
        // Only create RealPayroll when needed (lazy initialization)
        if (_realPayroll == null)
        {
            _realPayroll = new RealPayroll(_employeeId);
        }

        return _realPayroll.GetSalary();
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Client Code

// Client code
class Program
{
    static void Main(string[] args)
    {
        IPayroll payroll = new PayrollProxy("EMP123");

        // Salary is fetched and calculated only when requested
        Console.WriteLine("Proxy created, but salary not fetched yet.");
        Console.WriteLine($"Employee Salary: {payroll.GetSalary()}"); // Salary calculation triggered
        Console.WriteLine($"Employee Salary: {payroll.GetSalary()}"); // Cached result, no new fetch
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

Proxy created, but salary not fetched yet.
Fetching salary details from the payroll system...
Employee Salary: 5000.00
Employee Salary: 5000.00
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. RealPayroll: This class performs the actual salary calculation, but since it simulates an expensive operation, we don’t want it to run until necessary.
  2. PayrollProxy: This class controls when the real payroll calculation is triggered. The first time GetSalary() is called, it fetches the data. Subsequent calls reuse the result.
  3. Lazy Loading: With the proxy, the real object is only created when it is needed, helping save resources.

Example: Protection Proxy in a Payroll System

Now, let’s imagine we need to restrict access to salary data based on roles (e.g., only managers can view salaries). We can use a Protection Proxy to enforce access control.

Step 1: Define the Interface

public interface IEmployeeSalary
{
    decimal GetSalary();
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Implement the Real Payroll

public class EmployeeSalary : IEmployeeSalary
{
    private decimal _salary;

    public EmployeeSalary(decimal salary)
    {
        _salary = salary;
    }

    public decimal GetSalary()
    {
        return _salary;
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Implement the Protection Proxy

public class SalaryProtectionProxy : IEmployeeSalary
{
    private EmployeeSalary _realSalary;
    private string _role;

    public SalaryProtectionProxy(EmployeeSalary realSalary, string role)
    {
        _realSalary = realSalary;
        _role = role;
    }

    public decimal GetSalary()
    {
        if (_role != "Manager")
        {
            throw new UnauthorizedAccessException("Access Denied: Only managers can view employee salary.");
        }

        return _realSalary.GetSalary();
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Client Code

class Program
{
    static void Main(string[] args)
    {
        EmployeeSalary salary = new EmployeeSalary(6000.00M);
        IEmployeeSalary proxy = new SalaryProtectionProxy(salary, "Employee");

        try
        {
            Console.WriteLine($"Salary: {proxy.GetSalary()}");
        }
        catch (UnauthorizedAccessException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

Access Denied: Only managers can view employee salary.
Enter fullscreen mode Exit fullscreen mode

Explanation:

In this example, the SalaryProtectionProxy ensures that only managers can access employee salary data. This prevents unauthorized users from viewing sensitive information.


Conclusion

The Proxy Pattern is a versatile tool that can help developers manage access to resources, improve system performance, and enforce security policies. In a payroll system, this pattern allows for flexible, scalable design by implementing lazy loading, caching, and access control without complicating the core business logic.

Whether you need to control access to sensitive payroll data or delay the creation of expensive objects, the Proxy Pattern can help simplify your code and make it more maintainable.

Keep coding :)

Top comments (1)

Collapse
 
dotnetfullstackdev profile image
DotNet Full Stack Dev

@dazevedo Nice article, with a well-known example!
Published a Proxy pattern with a Book detail example.
Happy to hear from you about the article.
Proxy pattern