Revolutionizing API Development: A Journey Through Clean Architecture With Adapter Pattern in ASP.NET Core
Design patterns play a pivotal role in ensuring the maintainability, scalability, and flexibility of the codebase. One such pattern is the Adapter Design Pattern.
Join the DZone community and get the full member experience.
Join For FreeIn the realm of software development, design patterns play a pivotal role in ensuring the maintainability, scalability, and flexibility of the codebase. One such pattern is the Adapter Design Pattern, which allows the interface of an existing class to be used as another interface, facilitating the integration of disparate systems.
In this article, we'll explore how to implement the Adapter Design Pattern in an ASP.NET Core Web API using Clean Architecture. We'll use a model named DZoneArticles
with properties, and we'll cover the complete CRUD (Create, Read, Update, Delete) operations along with the implementation of business logic in all methods.
Prerequisites
Before diving into the implementation, make sure you have the following prerequisites:
- Visual Studio or Visual Studio Code
- .NET SDK installed (version 5.0 or later)
- Basic understanding of ASP.NET Core Web API and Clean Architecture
Step 1: Setting up the Project
Start by creating a new ASP.NET Core Web API project. Open your terminal or command prompt and run the following commands:
dotnet new webapi -n MyApi
cd APIDevelopmentWithCleanArchitectureAndAdapterPattern
Step 2: Creating the Clean Architecture Layers
Implementing Clean Architecture involves creating separate layers for different concerns. These layers typically include:
- Presentation Layer (API): Exposes endpoints and handles HTTP requests.
- Application Layer: Contains application-specific business logic.
- Domain Layer: Represents the core domain models and business rules.
- Infrastructure Layer: Deals with data access, external services, and other infrastructure concerns.
Create folders for each layer in your project and structure it accordingly.
Step 3: Defining the Model: DZoneArticles
In the Domain Layer, create a folder named Models
. Inside this folder, define the DZoneArticles
class with the required properties:
// Sardar Mudassar Ali Khan
// Domain/Models/DZoneArticles.cs
namespace APIDevelopmentWithCleanArchitectureAndAdapterPattern.Domain.Models
{
public class DZoneArticles
{
public int Id { get; set; }
public string Title { get; set; }
}
}
Step 4: Implementing the Repository Interface
In the Domain Layer, create a folder named Repositories
. Inside this folder, define an interface for the repository that will handle CRUD operations:
// Sardar Mudassar Ali Khan
// Domain/Repositories/IDZoneArticlesRepository.cs
using System.Collections.Generic;
using System.Threading.Tasks;
namespace APIDevelopmentWithCleanArchitectureAndAdapterPattern.Domain.Repositories
{
public interface IDZoneArticlesRepository
{
Task<IEnumerable<DZoneArticles>> GetAllAsync();
Task<DZoneArticles> GetByIdAsync(int id);
Task AddAsync(DZoneArticles article);
Task UpdateAsync(DZoneArticles article);
Task DeleteAsync(int id);
}
}
Step 5: Implementing the Repository
In the Infrastructure Layer, create a folder named Repositories
. Inside this folder, implement the IDZoneArticlesRepository
interface:
// Sardar Mudassar Ali Khan
// Infrastructure/Repositories/DZoneArticlesRepository.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; // Add this namespace
using MyApi.Domain.Models;
using MyApi.Domain.Repositories;
namespace APIDevelopmentWithCleanArchitectureAndAdapterPattern.Infrastructure.Repositories
{
public class DZoneArticlesRepository : IDZoneArticlesRepository
{
private readonly YourDbContext _dbContext; // Replace YourDbContext with your actual DbContext class
public DZoneArticlesRepository(YourDbContext dbContext) // Replace YourDbContext
{
_dbContext = dbContext;
}
public async Task<IEnumerable<DZoneArticles>> GetAllAsync()
{
return await _dbContext.DZoneArticles.ToListAsync();
}
public async Task<DZoneArticles> GetByIdAsync(int id)
{
return await _dbContext.DZoneArticles.FindAsync(id);
}
public async Task AddAsync(DZoneArticles article)
{
_dbContext.DZoneArticles.Add(article);
await _dbContext.SaveChangesAsync();
}
public async Task UpdateAsync(DZoneArticles article)
{
_dbContext.Entry(article).State = EntityState.Modified;
await _dbContext.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var articleToDelete = await _dbContext.DZoneArticles.FindAsync(id);
if (articleToDelete != null)
{
_dbContext.DZoneArticles.Remove(articleToDelete);
await _dbContext.SaveChangesAsync();
}
}
}
}
Step 6: Creating the Adapter
Create an adapter class that adapts the repository interface to the application's needs. This adapter will be in the Application Layer:
// Sardar Mudassar Ali Khan
// Application/Adapters/DZoneArticlesAdapter.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using MyApi.Domain.Models;
using MyApi.Domain.Repositories;
namespace APIDevelopmentWithCleanArchitectureAndAdapterPattern.Application.Adapters
{
public class DZoneArticlesAdapter
{
private readonly IDZoneArticlesRepository _repository;
public DZoneArticlesAdapter(IDZoneArticlesRepository repository)
{
_repository = repository;
}
public async Task<IEnumerable<DZoneArticles>> GetAllArticlesAsync()
{
return await _repository.GetAllAsync();
}
public async Task<DZoneArticles> GetArticleByIdAsync(int id)
{
return await _repository.GetByIdAsync(id);
}
public async Task AddArticleAsync(DZoneArticles article)
{
await _repository.AddAsync(article);
}
public async Task UpdateArticleAsync(DZoneArticles article)
{
await _repository.UpdateAsync(article);
}
public async Task DeleteArticleAsync(int id)
{
await _repository.DeleteAsync(id);
}
}
}
Step 7: Presentation Layer
In the API Layer, use dependency injection to inject the adapter into your controllers:
// Sardar Mudassar Ali Khan
// Presentation/Controllers/DZoneArticlesController.cs
using Microsoft.AspNetCore.Mvc;
using MyApi.Application.Adapters;
using MyApi.Domain.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace APIDevelopmentWithCleanArchitectureAndAdapterPattern.Presentation.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class DZoneArticlesController : ControllerBase
{
private readonly DZoneArticlesAdapter _adapter;
public DZoneArticlesController(DZoneArticlesAdapter adapter)
{
_adapter = adapter;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<DZoneArticles>>> GetAllArticles()
{
try
{
var articles = await _adapter.GetAllArticlesAsync();
return Ok(articles);
}
catch (Exception ex)
{
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
[HttpGet("{id}")]
public async Task<ActionResult<DZoneArticles>> GetArticleById(int id)
{
try
{
var article = await _adapter.GetArticleByIdAsync(id);
if (article == null)
{
return NotFound($"Article with ID {id} not found");
}
return Ok(article);
}
catch (Exception ex)
{
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
[HttpPost]
public async Task<ActionResult> AddArticle([FromBody] DZoneArticles article)
{
try
{
await _adapter.AddArticleAsync(article);
return CreatedAtAction(nameof(GetArticleById), new { id = article.Id }, article);
}
catch (Exception ex)
{
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
[HttpPut("{id}")]
public async Task<ActionResult> UpdateArticle(int id, [FromBody] DZoneArticles article)
{
try
{
if (id != article.Id)
{
return BadRequest("ID mismatch");
}
await _adapter.UpdateArticleAsync(article);
return NoContent();
}
catch (Exception ex)
{
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteArticle(int id)
{
try
{
await _adapter.DeleteArticleAsync(id);
return NoContent();
}
catch (Exception ex)
{
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
}
}
Ensure that you've registered your dependencies in the Startup.cs
file:
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Other configurations...
// Register repositories and adapters
services.AddScoped<IDZoneArticlesRepository, DZoneArticlesRepository>();
services.AddScoped<DZoneArticlesAdapter>();
// Other configurations...
}
Conclusion
Implementing the Adapter Design Pattern in an ASP.NET Core Web API with Clean Architecture involves creating well-defined layers, each responsible for a specific aspect of the application. By adhering to this architectural pattern, you ensure the separation of concerns, maintainability, and scalability of your codebase.
Remember to adapt the code examples to fit your specific use case and requirements. This guide provides a solid foundation, but customization based on your project's needs is crucial for a successful implementation.
Opinions expressed by DZone contributors are their own.
Comments