ASP.NET Core Web API Development with Observer Design Pattern

Creating a complete application with all code details and implementation for an ASP.NET Core Web API using the Observer Design Pattern is a comprehensive task. we will implement a high-level overview of each step, and you can expand on these steps with code implementations as needed.

Step 1. Create an ASP.NET Core Web API Project

Create a new ASP.NET Core Web API project in Visual Studio or with the .NET CLI. Ensure you have the necessary tools and SDKs installed.

dotnet new webapi -n TicketManagement

Step 2. Define the Layers


Presentation Layer (API)

Create a Controllers folder in your project to hold API controllers.

Implement controllers for managing tickets for example.

// Sardar Mudassar Ali Khan
using Microsoft.AspNetCore.Mvc;

[Route("api/[controller]")]
[ApiController]
public class TicketsController : ControllerBase
{
    private readonly ITicketService _ticketService;

    public TicketsController(ITicketService ticketService)
    {
        _ticketService = ticketService;
    }

    [HttpGet("{id}")]
    public IActionResult Get(int id)
    {
        try
        {
            var ticket = _ticketService.GetTicket(id);
            if (ticket != null)
            {
                return Ok(ticket);
            }
            else
            {
                return NotFound();
            }
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Internal Server Error: {ex.Message}");
        }
    }

    [HttpPost]
    public IActionResult Post([FromBody] Ticket ticket)
    {
        try
        {
            _ticketService.CreateTicket(ticket);
            return CreatedAtAction("Get", new { id = ticket.Id }, ticket);
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Internal Server Error: {ex.Message}");
        }
    }

    [HttpPut("{id}")]
    public IActionResult Put(int id, [FromBody] Ticket ticket)
    {
        try
        {
            var existingTicket = _ticketService.GetTicket(id);
            if (existingTicket == null)
            {
                return NotFound();
            }

            existingTicket.Title = ticket.Title;
            existingTicket.Description = ticket.Description;

            _ticketService.UpdateTicket(existingTicket);
            return NoContent();
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Internal Server Error: {ex.Message}");
        }
    }

    [HttpDelete("{id}")]
    public IActionResult Delete(int id)
    {
        try
        {
            var existingTicket = _ticketService.GetTicket(id);
            if (existingTicket == null)
            {
                return NotFound();
            }

            _ticketService.DeleteTicket(id);
            return NoContent();
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Internal Server Error: {ex.Message}");
        }
    }
}

Business Logic Layer (Service)

Create an interface ITicketService and a class TicketService that implements it. This class will include the Observer Pattern logic.

// Sardar Mudassar Ali Khan
using System;
using System.Collections.Generic;
using System.Linq;

public interface ITicketService
{
    void CreateTicket(Ticket ticket);
    Ticket GetTicket(int ticketId);
    void UpdateTicket(Ticket ticket);
    void DeleteTicket(int ticketId);
    void Subscribe(IObserver<Ticket> observer);
    void Unsubscribe(IObserver<Ticket> observer);
}

public class TicketService: ITicketService, IObservable<Ticket>
{
    private List<IObserver<Ticket> observers = new List<IObserver<Ticket>>();
    private List<Ticket> tickets = new List<Ticket>();

    public void CreateTicket(Ticket ticket)
    {
        tickets.Add(ticket);

        foreach (var observer in observers)
        {
            observer.OnNext(ticket);
        }
    }

    public Ticket GetTicket(int ticketId)
    {
        return tickets.FirstOrDefault(t => t.Id == ticketId);
    }

    public void UpdateTicket(Ticket ticket)
    {
        var existingTicket = GetTicket(ticket.Id);
        if (existingTicket != null)
        {
            existingTicket.Title = ticket.Title;
            existingTicket.Description = ticket.Description;

            foreach (var observer in observers)
            {
                observer.OnNext(existingTicket);
            }
        }
    }

    public void DeleteTicket(int ticketId)
    {
        var ticketToDelete = GetTicket(ticketId);
        if (ticketToDelete != null)
        {
            tickets.Remove(ticketToDelete);

            foreach (var observer in observers)
            {
                observer.OnNext(ticketToDelete);
            }
        }
    }

    public void Subscribe(IObserver<Ticket> observer)
    {
        if (!observers.Contains(observer))
        {
            observers.Add(observer);
        }
    }

    public void Unsubscribe(IObserver<Ticket> observer)
    {
        observers.Remove(observer);
    }

    public IDisposable Subscribe(IObserver<Ticket> observer)
    {
        if (!observers.Contains(observer))
        {
            observers.Add(observer);
        }
        return new Unsubscriber(observers, observer);
    }
}

Data Access Layer (Repository)

Create a repository interface and class for managing ticket data.

// Sardar Mudassar Ali khan
public interface ITicketRepository
{
    Ticket GetTicket(int ticketId);
    void CreateTicket(Ticket ticket);
    void UpdateTicket(Ticket ticket);
    void DeleteTicket(int ticketId);
}

public class TicketRepository : ITicketRepository
{
    private List<Ticket> ticketStore = new List<Ticket>();
    private int currentTicketId = 1;

    public Ticket GetTicket(int ticketId)
    {
        return ticketStore.FirstOrDefault(ticket => ticket.Id == ticketId);
    }

    public void CreateTicket(Ticket ticket)
    {
        ticket.Id = currentTicketId++;
        ticketStore.Add(ticket);
    }

    public void UpdateTicket(Ticket ticket)
    {
        var existingTicket = GetTicket(ticket.Id);
        if (existingTicket != null)
        {
            existingTicket.Title = ticket.Title;
            existingTicket.Description = ticket.Description;
        }
    }

    public void DeleteTicket(int ticketId)
    {
        var ticketToDelete = GetTicket(ticketId);
        if (ticketToDelete != null)
        {
            ticketStore.Remove(ticketToDelete);
        }
    }
}

Step 3. Implement the Observer Pattern

Create an Observer class that implements IObserver<Ticket> to handle notifications.

// Sardar Mudassar Ali Khan
public class TicketObserver: IObserver<Ticket>
{
    private IDisposable unsubscriber;

    public void OnCompleted()
    {
      // This Method Can be Implemented on the basis of application scope 
    }

    public void OnError(Exception error)
    {
      // This Method Can be Implemented on the basis of application scope 
    }

    public void OnNext(Ticket value)
    {
        // This Method Can be Implemented on the basis of application scope 
    }

    public void Subscribe(IObservable<Ticket> provider)
    {
        if (provider != null)
        {
            unsubscriber = provider.Subscribe(this);
        }
    }

    public void Unsubscribe()
    {
        unsubscriber.Dispose();
    }
}

Step 4. Dependency Injection

Register your services in the Startup.cs file using dependency injection.

public void ConfigureServices(IServiceCollection services)
{
    // Add services to the DI container.
    services.AddScoped<ITicketService, TicketService>();
    services.AddScoped<ITicketRepository, TicketRepository>();
    services.AddSingleton<IObserver<Ticket>, TicketObserver>();
}

Step 5. Implement the API Controllers

In the TicketsController, use the ITicketService to handle CRUD operations and notify observers when changes are made to tickets.

Step 6. Test Your Web API

You can use tools like Postman or Swagger to test your Web API by making HTTP requests to create, read, update, and delete tickets. Ensure that observers receive notifications when ticket changes occur.

This example provides a basic structure for implementing the Observer Design Pattern in an ASP.NET Core Web API with a 3-tier architecture for a Ticket Management system. You will need to fill in the details of your application's logic and data access according to your requirements and the technologies used.

Conclusion

In this implementation of an ASP.NET Core Web API with a 3-tier architecture and the Observer Design Pattern for a Ticket Management system, we have successfully created a structured application with the following components:

Presentation Layer (API)

This layer handles HTTP requests and responses. We've implemented a TicketsController to manage CRUD operations on tickets.

Business Logic Layer (Service): The TicketService class provides the core business logic for ticket management. It also implements the Observer Design Pattern to notify subscribers (observers) when changes are made to tickets.

Data Access Layer (Repository)

The TicketRepository class provides data access methods for managing ticket data. It can be implemented using Entity Framework or any other data access technology.

Observer Pattern: We've created a TicketObserver class that implements the IObserver<Ticket> interface to handle notifications when ticket changes occur. Subscribers can subscribe and unsubscribe from these notifications.

Dependency Injection

Services are registered in the Startup.cs file using dependency injection, making it easy to inject dependencies into controllers and services.

Testing

You can test your Web API using tools like Postman or Swagger by making HTTP requests to create, read, update, and delete tickets. Observers should receive notifications when changes are made to tickets.

This structure provides a scalable and maintainable solution for managing tickets while ensuring that changes to tickets are broadcast to interested subscribers. Depending on your specific requirements and the technologies used in your project, you may need to implement the data access layer using the appropriate technology and tailor the business logic accordingly.

The Observer Design Pattern is a powerful way to implement real-time updates and notifications in your application, making it suitable for scenarios where multiple components need to react to changes in data.