Building Scalable ASP.NET Core Web API with Onion Architecture and Abstract Factory Design Pattern

In Onion Architecture, layers are organized in a way that the core application logic is at the center, and external concerns are placed in outer layers. The common layers include Core, Infrastructure, and Presentation. Below are the steps to implement Onion Architecture with all layers for a CSharpCornerArticle model in an ASP.NET Core Web API:

Step 1. Create Project Structure

Create the following project structure:

  • ApiDevelopmentUsingAbstractFactoryPattern.Core: Core application logic
  • ApiDevelopmentUsingAbstractFactoryPattern.Infrastructure: Infrastructure concerns (data access, external services)
  • ApiDevelopmentUsingAbstractFactoryPattern.Web: Presentation layer (Web API controllers)

Step 2. Define Core Models

In the Core project, define the CSharpCornerArticle model.

// Sardar Mudassar Ali Khan
// ApiDevelopmentUsingAbstractFactoryPattern.Core/Models/CSharpCornerArticle.cs
public class CSharpCornerArticle
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    // Add other properties as needed
}

Step 3. Define Core Interfaces

Create interfaces for repositories and services in the Core project.

// Sardar Mudassar Ali Khan
// ApiDevelopmentUsingAbstractFactoryPattern.Core/Interfaces/ICSharpCornerArticleRepository.cs
public interface ICSharpCornerArticleRepository
{
    CSharpCornerArticle GetById(int id);
    IEnumerable<CSharpCornerArticle> GetAll();
    void Add(CSharpCornerArticle article);
    void Update(CSharpCornerArticle article);
    void Delete(int id);
}

For service Layer 

// Sardar Mudassar Ali Khan
// ApiDevelopmentUsingAbstractFactoryPattern.Core/Interfaces/ICSharpCornerArticleService.cs
public interface ICSharpCornerArticleService
{
    CSharpCornerArticle GetById(int id);
    IEnumerable<CSharpCornerArticle> GetAll();
    void Add(CSharpCornerArticle article);
    void Update(CSharpCornerArticle article);
    void Delete(int id);
}

Step 4. Implement Core Services

Implement the services in the Core project.

// Sardar Mudassar Ali Khan
// ApiDevelopmentUsingAbstractFactoryPattern.Core/Services/CSharpCornerArticleService.cs
public class CSharpCornerArticleService: ICSharpCornerArticleService
{
    private readonly ICSharpCornerArticleRepository _repository;

    public CSharpCornerArticleService(ICSharpCornerArticleRepository repository)
    {
        _repository = repository;
    }

    public CSharpCornerArticle GetById(int id) => _repository.GetById(id);

    public IEnumerable<CSharpCornerArticle> GetAll() => _repository.GetAll();

    public void Add(CSharpCornerArticle article) => _repository.Add(article);

    public void Update(CSharpCornerArticle article) => _repository.Update(article);

    public void Delete(int id) => _repository.Delete(id);
}

Step 5. Infrastructure - Implement Repository

In the Infrastructure project, implement the repository.

// Sardar Mudassar Ali Khan
//ApiDevelopmentUsingAbstractFactoryPattern.Infrastructure/Repositories/CSharpCornerArticleRepository.cs
public class CSharpCornerArticleRepository: ICSharpCornerArticleRepository
{
    private readonly AppDbContext _context;

    public CSharpCornerArticleRepository(AppDbContext context)
    {
        _context = context;
    }

    public CSharpCornerArticle GetById(int id) => _context.CSharpCornerArticles.Find(id);

    public IEnumerable<CSharpCornerArticle> GetAll() => _context.CSharpCornerArticles.ToList();

    public void Add(CSharpCornerArticle article)
    {
        _context.CSharpCornerArticles.Add(article);
        _context.SaveChanges();
    }

    public void Update(CSharpCornerArticle article)
    {
        _context.CSharpCornerArticles.Update(article);
        _context.SaveChanges();
    }

    public void Delete(int id)
    {
        var article = GetById(id);
        if (article != null)
        {
            _context.CSharpCornerArticles.Remove(article);
            _context.SaveChanges();
        }
    }
}

Step 6. Infrastructure - Dependency Injection

Configure dependency injection in the Startup.cs of the Web project.

// Sardar Mudassar Ali Khan
// ApiDevelopmentUsingAbstractFactoryPattern.Web/Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    // Other configurations

    services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddScoped<ICSharpCornerArticleRepository, CSharpCornerArticleRepository>();
    services.AddScoped<ICSharpCornerArticleService, CSharpCornerArticleService>();
}

Step 7. Web - Implement Controllers

In the Web project, implement the Web API controllers.

// Sardar Mudassar Ali Khan
// ApiDevelopmentUsingAbstractFactoryPattern.Web/Controllers/CSharpCornerArticleController.cs
[ApiController]
[Route("api/[controller]")]
public class CSharpCornerArticleController : ControllerBase
{
    private readonly ICSharpCornerArticleService _service;

    public CSharpCornerArticleController(ICSharpCornerArticleService service)
    {
        _service = service;
    }

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

    [HttpGet]
    public IActionResult GetAll()
    {
        var articles = _service.GetAll();
        return Ok(articles);
    }

    [HttpPost]
    public IActionResult Post([FromBody] CSharpCornerArticle article)
    {
        _service.Add(article);
        return CreatedAtAction(nameof(Get), new { id = article.Id }, article);
    }

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

        article.Id = id;
        _service.Update(article);
        return NoContent();
    }

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

        _service.Delete(id);
        return NoContent();
    }
}

Step 8. Run and Test

Run the application and test the CRUD operations using tools like Postman or Swagger.

This implementation follows the Onion Architecture principles, keeping the core logic separated from infrastructure concerns. Adjustments and improvements can be made based on specific requirements and additional features.

Conclusion

The implementation of the Abstract Factory Design Pattern within the Onion Architecture for an ASP.NET Core Web API, focusing on the CSharpCornerArticle model, provides a structured and modular approach to building scalable and maintainable applications. Let's recap the key aspects of the implementation:

1. Onion Architecture Overview

  1. Core Layer: Contains the application's business logic and entities (e.g., CSharpCornerArticle).
  2. Infrastructure Layer: Houses external concerns like data access, repositories, and database context.
  3. Web Layer (Presentation): Consists of Web API controllers, serving as the entry point for external interactions.

2. Abstract Factory Design Pattern

  1. IRepositoryFactory: An abstract factory interface defining methods for creating repositories.
  2. RepositoryFactory: A concrete factory implementing IRepositoryFactory, responsible for creating concrete repository instances.

3. Model

  1. CSharpCornerArticle: Represents the core entity with properties relevant to articles.

4. Data Access and Repository

  1. AppDbContext: Manages the database context and entity configurations.
  2. ICSharpCornerArticleRepository: An interface defining CRUD operations for the CSharpCornerArticle entity.
  3. CSharpCornerArticleRepository: Implements the repository interface and handles data access.

5. Dependency Injection

  1. Configured in the Startup.cs file in the Web layer to inject repository and service dependencies.

6. Service Layer

  1. ICSharpCornerArticleService: An interface defining business logic operations for CSharpCornerArticle
  2. CSharpCornerArticleService: Implements the service interface, providing the core application logic.

7. Web API Controllers

  1. CSharpCornerArticleController: Implements CRUD operations, interacting with the service layer.

8. Testing

  1. Suggested using tools like Postman or Swagger to test CRUD operations.

9. Conclusion

  1. The implementation follows the principles of Onion Architecture, promoting separation of concerns and maintainability.
  2. The Abstract Factory pattern facilitates the creation of repository instances, promoting flexibility and testability.
  3. The use of interfaces and dependency injection enhances modularity and allows for easy unit testing.
  4. The structured layers enable scalability and ease of maintenance as the application grows.

This implementation serves as a foundation for building robust, modular, and scalable ASP.NET Core Web APIs. As the project evolves, additional features, security measures, and optimizations can be incorporated while adhering to the principles of the chosen architectural style.