Decoding Clean Architecture Implementing the Bridge Pattern and API Operations in ASP.NET Core Web API

I will implement the Bridge Pattern in an ASP.NET Core Web API using Clean Architecture. We'll create a simple CRUD (Create, Read, Update, Delete) API for managing DZone articles. In Clean Architecture, we'll have the following layers:

  1. Web API Layer (Presentation)
  2. Application Layer 
  3. Domain Layer (Entities and Business Logic)
  4. Infrastructure Layer (Data Access and External Services)

Let's start by defining the layers and then implement the Bridge Pattern.

1. Domain Layer

Define Model 

// Sardar Mudassar Ali Khan
namespace CleanArchitecture.Domain.Entities
{
    public class DZoneArticle
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    }
}
// Sardar Mudassar Ali Khan
// IDZoneArticleRepository.cs
namespace CleanArchitecture.Domain.Interfaces
{
    public interface IDZoneArticleRepository
    {
        Task<DZoneArticle> GetByIdAsync(int id);
        Task<IEnumerable<DZoneArticle>> GetAllAsync();
        Task<int> CreateAsync(DZoneArticle article);
        Task UpdateAsync(DZoneArticle article);
        Task DeleteAsync(int id);
    }
}

2. Infrastructure Layer

// Sardar Mudassar Ali Khan
// DZoneArticleRepository.cs
namespace CleanArchitecture.Infrastructure.Data
{
    public class DZoneArticleRepository: IDZoneArticleRepository
    {
        private readonly ApplicationDbContext _dbContext;

        public DZoneArticleRepository(ApplicationDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public async Task<DZoneArticle> GetByIdAsync(int id)
        {
            return await _dbContext.DZoneArticles.FindAsync(id);
        }

        public async Task<IEnumerable<DZoneArticle>> GetAllAsync()
        {
            return await _dbContext.DZoneArticles.ToListAsync();
        }

        public async Task<int> CreateAsync(DZoneArticle article)
        {
            _dbContext.DZoneArticles.Add(article);
            await _dbContext.SaveChangesAsync();
            return article.Id;
        }

        public async Task UpdateAsync(DZoneArticle article)
        {
            _dbContext.Entry(article).State = EntityState.Modified;
            await _dbContext.SaveChangesAsync();
        }

        public async Task DeleteAsync(int id)
        {
            var article = await _dbContext.DZoneArticles.FindAsync(id);
            if (article != null)
            {
                _dbContext.DZoneArticles.Remove(article);
                await _dbContext.SaveChangesAsync();
            }
        }
    }
}

3. Infrastructure Layer (Bridge Interface)

// Sardar Mudassar Ali Khan
// IUnitOfWork.cs
namespace CleanArchitecture.Infrastructure.Data
{
    public interface IUnitOfWork
    {
        Task<int> SaveChangesAsync();
    }
}

4. Infrastructure Layer (Bridge Implementation)

// Sardar Mudassar Ali Khan
// UnitOfWork.cs
namespace CleanArchitecture.Infrastructure.Data
{
    public class UnitOfWork : IUnitOfWork
    {
        private readonly ApplicationDbContext _dbContext;

        public UnitOfWork(ApplicationDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public async Task<int> SaveChangesAsync()
        {
            return await _dbContext.SaveChangesAsync();
        }
    }
}

5. Application Layer

// Sardar Mudassar Ali Khan
// DZoneArticleService.cs
namespace CleanArchitecture.Application.Services
{
    public class DZoneArticleService
    {
        private readonly IDZoneArticleRepository _repository;

        public DZoneArticleService(IDZoneArticleRepository repository)
        {
            _repository = repository;
        }

        public async Task<DZoneArticle> GetArticleByIdAsync(int id)
        {
            return await _repository.GetByIdAsync(id);
        }

        public async Task<IEnumerable<DZoneArticle>> GetAllArticlesAsync()
        {
            return await _repository.GetAllAsync();
        }

        public async Task<int> CreateArticleAsync(DZoneArticle article)
        {
            return await _repository.CreateAsync(article);
        }

        public async Task UpdateArticleAsync(DZoneArticle article)
        {
            await _repository.UpdateAsync(article);
        }

        public async Task DeleteArticleAsync(int id)
        {
            await _repository.DeleteAsync(id);
        }
    }
}

6. Web API Layer (Presentation Layer1)

// Sardar Mudassar Ali Khan
// DZoneArticlesController.cs
namespace CleanArchitecture.WebApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class DZoneArticlesController : ControllerBase
    {
        private readonly DZoneArticleService _articleService;

        public DZoneArticlesController(DZoneArticleService articleService)
        {
            _articleService = articleService;
        }

        [HttpGet]
        public async Task<IActionResult> GetAll()
        {
            var articles = await _articleService.GetAllArticlesAsync();
            return Ok(articles);
        }

        [HttpGet("{id}")]
        public async Task<IActionResult> GetById(int id)
        {
            var article = await _articleService.GetArticleByIdAsync(id);
            if (article == null)
                return NotFound();

            return Ok(article);
        }

        [HttpPost]
        public async Task<IActionResult> Create([FromBody] DZoneArticle article)
        {
            var id = await _articleService.CreateArticleAsync(article);
            return CreatedAtAction(nameof(GetById), new { id }, article);
        }

        [HttpPut("{id}")]
        public async Task<IActionResult> Update(int id, [FromBody] DZoneArticle article)
        {
            if (id != article.Id)
                return BadRequest();

            await _articleService.UpdateArticleAsync(article);
            return NoContent();
        }

        [HttpDelete("{id}")]
        public async Task<IActionResult> Delete(int id)
        {
            await _articleService.DeleteArticleAsync(id);
            return NoContent();
        }
    }
}

This example demonstrates a simple implementation of the Bridge Pattern in a Clean Architecture-based ASP.NET Core Web API for managing DZone articles. The Bridge Pattern is indirectly applied by using repository interfaces (IDZoneArticleRepository) and their implementations (DZoneArticleRepository). This separation allows the application to easily switch between different data access strategies without affecting the higher-level logic.

Conclusion

In conclusion, the implementation showcased a practical application of Clean Architecture principles and the Bridge Pattern in the context of building a robust ASP.NET Core Web API for managing DZoneArticle entities. The separation of concerns into distinct layers Domain, Infrastructure, Application, and Web API reflects a clean and modular design that facilitates maintainability, scalability, and testability.

The Bridge Pattern was effectively employed in the Infrastructure layer, where the IDZoneArticleRepository served as the abstraction, and the DZoneArticleRepository acted as the refined abstraction, relying on the IUnitOfWork interface as the bridge. This separation allows for flexibility and extensibility, making it easy to switch or add different data storage strategies without affecting the higher-level application logic.

By adhering to Clean Architecture principles, the codebase promotes a clear separation of concerns, ensuring that business logic is isolated from implementation details. This not only enhances code maintainability but also facilitates unit testing and the evolution of the application over time.

In summary, the presented implementation demonstrates a harmonious blend of Clean Architecture and the Bridge Pattern, providing a solid foundation for building scalable and maintainable ASP.NET Core Web APIs.


Similar Articles