DEV Community

Cover image for Using the Observer Pattern in Payroll Processing
Daniel Azevedo
Daniel Azevedo

Posted on

Using the Observer Pattern in Payroll Processing

Hi devs,

For those who aren’t familiar, the Observer Pattern is a behavioral design pattern that allows an object (called the subject) to notify other objects (observers) when its state changes. This is great when you want to maintain a one-to-many relationship between objects but don’t want them tightly coupled.

Let me share how this pattern played a role in improving our payroll system and streamlining notifications and updates in HR.

The Problem: Multiple Systems Needing Updates

In a typical HR or payroll system, there are a lot of interconnected processes that need to respond to changes. For example, imagine a scenario where employee salaries are calculated based on their performance, bonuses, and hours worked. Once payroll is processed, several departments or services might need to be notified, such as:

  • The Finance department for payout approvals
  • The HR system for updating employee records
  • The Employee dashboard for salary slips
  • The Tax system for tax calculation updates

Manually wiring all these parts together makes the system highly coupled and difficult to maintain. Every time a change is made, you would have to update each dependent service or system manually. This is where the Observer Pattern can really shine.

How the Observer Pattern Solves This

With the Observer Pattern, when the payroll is processed, it triggers updates in all the necessary systems automatically. Each system (Finance, HR, Employee Dashboard, etc.) can subscribe as an observer and receive notifications without needing to know the internal workings of the payroll system. This creates a loosely coupled system, where each service can respond to changes independently.

Example: Payroll Processing with the Observer Pattern in C

Here’s how we implemented this pattern in our payroll system, using C#:

First, we define the subject, which in this case is the PayrollProcessor. This class will notify observers whenever payroll is processed:

public class PayrollProcessor
{
    private readonly List<IPayrollObserver> _observers = new List<IPayrollObserver>();

    public void AddObserver(IPayrollObserver observer)
    {
        _observers.Add(observer);
    }

    public void RemoveObserver(IPayrollObserver observer)
    {
        _observers.Remove(observer);
    }

    public void ProcessPayroll(Employee employee)
    {
        // Process payroll logic here (calculating salary, bonuses, etc.)
        Console.WriteLine($"Processing payroll for {employee.Name}");

        // Notify all observers
        foreach (var observer in _observers)
        {
            observer.Update(employee);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, we define the IPayrollObserver interface that any observer will implement:

public interface IPayrollObserver
{
    void Update(Employee employee);
}
Enter fullscreen mode Exit fullscreen mode

Then, we create observers like FinanceDepartment, HRSystem, and EmployeeDashboard, which will respond to payroll changes:

public class FinanceDepartment : IPayrollObserver
{
    public void Update(Employee employee)
    {
        Console.WriteLine($"Finance department notified for {employee.Name}'s payroll.");
        // Logic for managing salary payouts
    }
}

public class HRSystem : IPayrollObserver
{
    public void Update(Employee employee)
    {
        Console.WriteLine($"HR system updated for {employee.Name}'s payroll.");
        // Logic for updating employee records
    }
}

public class EmployeeDashboard : IPayrollObserver
{
    public void Update(Employee employee)
    {
        Console.WriteLine($"{employee.Name} can view their updated salary slip.");
        // Logic for displaying salary slip to the employee
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally, in the Main method, we can wire this all together:

public class Employee
{
    public string Name { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        var employee = new Employee { Name = "John Doe" };
        var payrollProcessor = new PayrollProcessor();

        var finance = new FinanceDepartment();
        var hr = new HRSystem();
        var dashboard = new EmployeeDashboard();

        // Add observers
        payrollProcessor.AddObserver(finance);
        payrollProcessor.AddObserver(hr);
        payrollProcessor.AddObserver(dashboard);

        // Process payroll
        payrollProcessor.ProcessPayroll(employee);
    }
}
Enter fullscreen mode Exit fullscreen mode

The Benefits We Saw

  1. Loose Coupling: By using the Observer Pattern, the PayrollProcessor doesn’t need to know who’s interested in payroll changes. It simply notifies all registered observers. This means we can easily add or remove new systems (like Tax, Benefits, or Performance Review) without changing the core payroll logic.

  2. Scalability: As the business grows and more systems need to react to payroll updates, we don’t have to worry about adding complexity to the payroll processor itself. New services can be added as observers without modifying the existing code.

  3. Maintainability: The separation of concerns between the payroll processing logic and the systems that react to it made the system much easier to maintain. For example, changes in how the HR system handles payroll do not affect the payroll processing code.

When to Use the Observer Pattern

From my experience, the Observer Pattern is really useful in scenarios where you have:

  • One-to-many dependencies: When multiple systems need to be notified of changes in a single entity (e.g., payroll processing).
  • Loose coupling: When you don’t want tightly coupled systems, especially when each system should act independently.

In our case, the Observer Pattern turned out to be a perfect fit for payroll processing, since it allowed us to build a flexible and scalable solution for notifying various departments and systems whenever payroll is processed.

Final Thoughts

The Observer Pattern has proven to be a powerful tool in our payroll processing system, making it much easier to scale and maintain over time. While it may not be the best solution for every scenario, it works wonders when you need to manage complex, interconnected systems like payroll or human resources.

If you're working on a similar system where multiple services need to react to the same event, I highly recommend giving the Observer Pattern a try. It might just make your life a lot easier!

keep coding :)

Top comments (0)