ASP.NET Core Web API Development with Template Method Pattern and 3-Tier Architecture

To implement the Template Method Pattern in an ASP.NET Core Web API with a 3-tier architecture, I need to create layers for presentation, business logic, and data access. Additionally, I need to create a model for CSharpCornerArticle. This design pattern allows for a structured, modular, and easily maintainable architecture by separating concerns into distinct layers and leveraging the Template Method Pattern to provide a common structure for CRUD operations while allowing flexibility for additional logic in concrete implementations.

Create the ASP.NET Core Web API Project

Start by creating a new ASP.NET Core Web API project in Visual Studio or your preferred development environment.

Create Layers

Implement the 3-tier architecture by creating separate projects or folders for each layer.

Presentation Layer: Controllers and DTOs.
Business Logic Layer: Services.
Data Access Layer: Repositories and the data model.
Model (CSharpArticle):

Create a model class representing the CSharpArticle.

// Sardar Mudassar Ali Khan
// CSharpArticle.cs
public class CSharpArticle
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}

Data Access Layer (Repository)

Create a repository interface and implementation for data access.

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

public interface IRepository<T>
{
    T GetById(int id);
    IEnumerable<T> GetAll();
    void Add(T entity);
    void Update(T entity);
    void Delete(int id);
}

public class CSharpArticleRepository : IRepository<CSharpArticle>
{
    private readonly List<CSharpArticle> _data; // Simulated in-memory storage, replace this with your actual data source

    public CSharpArticleRepository()
    {
        _data = new List<CSharpArticle>();
        // Example initial data for simulation
        _data.Add(new CSharpArticle { Id = 1, Title = "Sample Title 1", Content = "Sample Content 1" });
        _data.Add(new CSharpArticle { Id = 2, Title = "Sample Title 2", Content = "Sample Content 2" });
    }

    public CSharpArticle GetById(int id)
    {
        return _data.FirstOrDefault(article => article.Id == id);
    }

    public IEnumerable<CSharpArticle> GetAll()
    {
        return _data;
    }

    public void Add(CSharpArticle entity)
    {
        if (entity == null)
        {
            throw new ArgumentNullException(nameof(entity));
        }
        entity.Id = _data.Any() ? _data.Max(a => a.Id) + 1 : 1;
        _data.Add(entity);
    }

    public void Update(CSharpArticle entity)
    {
        if (entity == null)
        {
            throw new ArgumentNullException(nameof(entity));
        }
        var existing = _data.FirstOrDefault(a => a.Id == entity.Id);
        if (existing != null)
        {
            existing.Title = entity.Title;
            existing.Content = entity.Content;
        }
    }

    public void Delete(int id)
    {
        var entity = _data.FirstOrDefault(a => a.Id == id);
        if (entity != null)
        {
            _data.Remove(entity);
        }
    }
}

public class CSharpArticle
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}

Business Logic Layer (Service)

Create a service that will use the Template Method Pattern.

// Sardar Mudassar Ali Khan
public abstract class CSharpArticleService
{
    protected readonly IRepository<CSharpArticle> repository;

    public CSharpArticleService(IRepository<CSharpArticle> repository)
    {
        this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
    }

    public CSharpArticle GetArticle(int id)
    {
        if (id <= 0)
        {
            throw new ArgumentException("Invalid ID");
        }
        return repository.GetById(id);
    }

    public IEnumerable<CSharpArticle> GetAllArticles()
    {
        return repository.GetAll();
    }

    public void CreateArticle(CSharpArticle article)
    {
        if (article == null)
        {
            throw new ArgumentNullException(nameof(article));
        }
        repository.Add(article);
    }

    public void UpdateArticle(CSharpArticle article)
    {
        if (article == null)
        {
            throw new ArgumentNullException(nameof(article));
        }
        if (repository.GetById(article.Id) == null)
        {
            throw new InvalidOperationException("Article does not exist");
        }
        repository.Update(article);
    }

    public void DeleteArticle(int id)
    {
        if (id <= 0)
        {
            throw new ArgumentException("Invalid ID");
        }
        repository.Delete(id);
    }
}

public class ConcreteCSharpArticleService : CSharpArticleService
{
    public ConcreteCSharpArticleService(IRepository<CSharpArticle> repository) : base(repository)
    {
        this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
    }

    public IEnumerable<CSharpArticle> GetArticlesWithContentLengthGreaterThan(int length)
    {
        return repository.GetAll().Where(article => article.Content.Length > length);
    }

    public void PublishArticle(int id)
    {
        var article = repository.GetById(id);
        if (article == null)
        {
            throw new ArgumentException("Article not found");
        }
       
    }

    public void ArchiveArticle(int id)
    {
        var article = repository.GetById(id);
        if (article == null)
        {
            throw new ArgumentException("Article not found");
        }
        
    }

}

Presentation Layer (Controller)

Create a controller for handling HTTP requests.

// Sardar Mudassar Ali Khan
// CSharpArticleController.cs
[Route("api/articles")]
[ApiController]
public class CSharpArticleController : ControllerBase
{
    private readonly CSharpArticleService articleService;

    public CSharpArticleController(CSharpArticleService articleService)
    {
        this.articleService = articleService;
    }

    [HttpGet]
    public IActionResult Get()
    {
        var articles = articleService.GetAllArticles();
        return Ok(articles);
    }

    [HttpGet("{id}")]
    public IActionResult Get(int id)
    {
        var article = articleService.GetArticle(id);
        if (article == null)
            return NotFound();
        return Ok(article);
    }

    [HttpPost]
    public IActionResult Post([FromBody] CSharpArticle article)
    {
        articleService.CreateArticle(article);
        return Created($"/api/articles/{article.Id}", article);
    }

    [HttpPut("{id}")]
    public IActionResult Put(int id, [FromBody] CSharpArticle article)
    {
        var existingArticle = articleService.GetArticle(id);
        if (existingArticle == null)
            return NotFound();

        article.Id = id;
        articleService.UpdateArticle(article);
        return NoContent();
    }

    [HttpDelete("{id}")]
    public IActionResult Delete(int id)
    {
        var existingArticle = articleService.GetArticle(id);
        if (existingArticle == null)
            return NotFound();

        articleService.DeleteArticle(id);
        return NoContent();
    }
}

Dependency Injection Configuration

Register your services and repositories in the Startup.cs.

// Sardar Mudassar Ali Khan
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IRepository<CSharpArticle>, CSharpArticleRepository>();
    services.AddScoped<CSharpArticleService, ConcreteCSharpArticleService>();
    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Conclusion

The implementation of the Template Method Pattern within an ASP.NET Core Web API with a 3-tier architecture offers a structured and flexible approach for handling CRUD operations on the `CSharpArticle` model. Here's a summary of the key points:

1. Design Pattern Implementation: The Template Method Pattern is used to define a skeletal structure for performing CRUD operations, offering flexibility to extend or modify specific parts of the process while keeping the overall structure intact.

2. Layered Architecture:

  • Presentation Layer: Controllers handle HTTP requests and responses.
  • Business Logic Layer: Services manage the business logic, leveraging the Template Method Pattern for CRUD operations.
  • Data Access Layer: Repositories interact with the data storage, handling actual database operations.

3. Cohesive Functionality:

  • The abstract CSharpArticleService class provides a standard interface for CRUD operations on CSharpArticle.
  • The concrete implementation, `ConcreteCSharpArticleService`, extends the abstract class, allowing for additional methods tailored to specific business needs.

4. Error Handling and Validation:

  • Proper checks and validations ensure data integrity and security, preventing potential issues such as null references and invalid operations.

5. Scalability and Extensibility:

  • The modular structure facilitates scalability and maintenance, enabling the addition of new features or alterations without affecting the core structure.

By adhering to this architecture, developers can maintain a clear separation of concerns, streamline development, and easily adapt the system to changing requirements, making it a robust foundation for ASP.NET Core Web API development.


Similar Articles