IntraDotNet.CleanArchitecture.Application 1.0.0

dotnet add package IntraDotNet.CleanArchitecture.Application --version 1.0.0
                    
NuGet\Install-Package IntraDotNet.CleanArchitecture.Application -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="IntraDotNet.CleanArchitecture.Application" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="IntraDotNet.CleanArchitecture.Application" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="IntraDotNet.CleanArchitecture.Application" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add IntraDotNet.CleanArchitecture.Application --version 1.0.0
                    
#r "nuget: IntraDotNet.CleanArchitecture.Application, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package IntraDotNet.CleanArchitecture.Application@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=IntraDotNet.CleanArchitecture.Application&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=IntraDotNet.CleanArchitecture.Application&version=1.0.0
                    
Install as a Cake Tool

IntraDotNet.CleanArchitecture.Application

.NET C# License

Table Of Contents

Overview

The IntraDotNet.CleanArchitecture.Application library provides a standardised foundation for the application layer in Clean Architecture projects. This layer orchestrates your use cases, coordinates domain logic, and defines the contracts that infrastructure must implement.

The Application Layer sits between the Domain and Infrastructure layers:

  • Depends on Domain - Uses domain entities and business rules
  • Defines Infrastructure Contracts - Specifies what it needs (repositories, services)
  • Independent of Frameworks - No dependency on databases, web frameworks, or external systems
  • Contains Use Cases - Implements application-specific business logic

What's Included

  • Repository Interfaces: Define contracts for data persistence (implemented by Infrastructure layer)
  • Unit of Work Pattern: Ensures transactional consistency across operations
  • Result Pattern: Type-safe handling of success/failure without exceptions
  • Base Services: Reusable service implementations for common CRUD operations
  • Validation Services: Services with built-in validation hooks
  • User Context Interface: Contract for accessing current user information

Key Concepts

Application Layer Responsibilities

The Application Layer is responsible for:

  • 📋 Use Case Implementation - Orchestrating domain logic to fulfill business requirements
  • 🔄 Transaction Management - Coordinating multiple repository operations
  • Input Validation - Validating data before domain operations
  • 🎭 DTO Mapping - Converting between domain entities and presentation models (in derived services)
  • 📝 Defining Contracts - Specifying what infrastructure must provide

What Doesn't Belong Here

The Application Layer should NOT contain:

  • ❌ Database implementations (belongs in Infrastructure)
  • ❌ HTTP/API concerns (belongs in Presentation/API)
  • ❌ Domain business rules (belongs in Domain)
  • ❌ External service calls (belongs in Infrastructure)

Project Structure

Common/
  ├── Persistence/
  │   ├── IGuidRepository.cs         # GUID-based repository contract
  │   ├── IIntRepository.cs          # Integer-based repository contract
  │   └── IUnitOfWork.cs             # Transaction management contract
  └── Services/
      ├── ICurrentUserService.cs     # User context contract
      ├── IGuidDataService.cs        # GUID service contract
      ├── IIntDataService.cs         # Integer service contract
      ├── IGuidValidatableDataService.cs
      └── IIntValidatableDataService.cs

Results/
  ├── Result.cs                      # Success/failure without value
  └── ValueResult.cs                 # Success/failure with value

Services/
  ├── GuidDataService.cs             # Base GUID service implementation
  ├── IntDataService.cs              # Base Int service implementation
  ├── GuidValidatableDataService.cs  # GUID service with validation
  └── IntValidatableDataService.cs   # Int service with validation

Core Components

1. Repository Interfaces

Repository interfaces define the contract for data access without specifying implementation details.

IGuidRepository<TEntity>

public interface IGuidRepository<TEntity> where TEntity : class, IGuidIdentifier
{
    Task<TEntity?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
    Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = default);
    Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
    Task AddAsync(TEntity entity, CancellationToken cancellationToken = default);
    void Update(TEntity entity);
    void Delete(TEntity entity);
}

IIntRepository<TEntity>

public interface IIntRepository<TEntity> where TEntity : class, IIntIdentifier
{
    Task<TEntity?> GetByIdAsync(int id, CancellationToken cancellationToken = default);
    Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = default);
    Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
    Task AddAsync(TEntity entity, CancellationToken cancellationToken = default);
    void Update(TEntity entity);
    void Delete(TEntity entity);
}

2. Unit of Work Pattern

Ensures multiple operations can be executed as a single transaction.

public interface IUnitOfWork
{
    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
    Task BeginTransactionAsync(CancellationToken cancellationToken = default);
    Task CommitTransactionAsync(CancellationToken cancellationToken = default);
    Task RollbackTransactionAsync(CancellationToken cancellationToken = default);
}

3. Result Pattern

Provides type-safe error handling without exceptions.

Result - For operations without return values:

var result = Result.Success();
var result = Result.Failure("Something went wrong");
var result = Result.Failure(new[] { "Error 1", "Error 2" });

ValueResult<T> - For operations with return values:

var result = ValueResult<Product>.Success(product);
var result = ValueResult<Product>.Failure("Product not found");

Checking Results:

if (result.IsSuccess)
{
    var value = result.Value;
    // Use value
}
else
{
    var error = result.Error; // Single error message
    var errors = result.AggregateErrors; // Multiple errors
}

4. Base Services

Pre-built service implementations for common CRUD operations.

GuidDataService<TEntity> - For entities with GUID identifiers IntDataService<TEntity> - For entities with integer identifiers

Both provide:

  • CreateAsync() - Create new entity
  • UpdateAsync() - Update existing entity
  • DeleteAsync() - Delete entity (supports soft delete)
  • DeleteByIdAsync() - Delete by ID
  • GetAllAsync() - Get all entities
  • GetByIdAsync() - Get entity by ID
  • FindAsync() - Find entities matching criteria

5. Validatable Services

Services with built-in validation hooks.

GuidValidatableDataService<TEntity> IntValidatableDataService<TEntity>

These extend the base services and add:

  • ValidateAsync() - Custom validation logic
  • Automatic validation before Create/Update operations

6. Current User Service

Interface for accessing authenticated user information (implemented in Infrastructure/Presentation layer).

public interface ICurrentUserService
{
    string? UserId { get; }
    string? UserName { get; }
    bool IsAuthenticated { get; }
}

Complete Examples

Example 1: Basic Repository and Service

Step 1: Define Custom Repository Interface
// YourApp.Application/Interfaces/Persistence/IProductRepository.cs
using IntraDotNet.CleanArchitecture.Application.Common.Persistence;
using YourApp.Domain.Entities;

namespace YourApp.Application.Interfaces.Persistence;

public interface IProductRepository : IGuidRepository<Product>
{
    Task<Product?> GetByNameAsync(string name, CancellationToken cancellationToken = default);
    Task<Product?> GetBySKUAsync(string sku, CancellationToken cancellationToken = default);
    Task<IEnumerable<Product>> GetProductsInPriceRangeAsync(decimal min, decimal max, CancellationToken cancellationToken = default);
    Task<bool> ExistsBySKUAsync(string sku, Guid? excludeId = null, CancellationToken cancellationToken = default);
}
Step 2: Create Service Interface
// YourApp.Application/Interfaces/Services/IProductService.cs
using IntraDotNet.CleanArchitecture.Application.Common.Services;
using YourApp.Domain.Entities;

namespace YourApp.Application.Interfaces.Services;

public interface IProductService : IGuidDataService<Product>
{
    Task<ValueResult<IEnumerable<Product>>> GetProductsInPriceRangeAsync(
        decimal minPrice, 
        decimal maxPrice, 
        CancellationToken cancellationToken = default);
}
Step 3: Implement Service with Validation
// YourApp.Application/Services/ProductService.cs
using System.Linq.Expressions;
using IntraDotNet.CleanArchitecture.Application.Services;
using IntraDotNet.CleanArchitecture.Application.Results;
using IntraDotNet.CleanArchitecture.Application.Common.Persistence;
using YourApp.Application.Interfaces.Persistence;
using YourApp.Application.Interfaces.Services;
using YourApp.Domain.Entities;

namespace YourApp.Application.Services;

public class ProductService : GuidValidatableDataService<Product>, IProductService
{
    private readonly IProductRepository _repository;
    private readonly IUnitOfWork _unitOfWork;

    public ProductService(
        IProductRepository repository,
        IUnitOfWork unitOfWork)
    {
        _repository = repository;
        _unitOfWork = unitOfWork;
    }

    #region Validation

    public override async Task<ValueResult<bool>> ValidateAsync(
        Product entity, 
        CancellationToken cancellationToken = default)
    {
        var errors = new List<string>();

        // Required field validation
        if (string.IsNullOrWhiteSpace(entity.Name))
            errors.Add("Product name is required");

        if (string.IsNullOrWhiteSpace(entity.SKU))
            errors.Add("Product SKU is required");

        // Business rule validation
        if (entity.Price <= 0)
            errors.Add("Product price must be greater than zero");

        if (entity.StockQuantity < 0)
            errors.Add("Stock quantity cannot be negative");

        // Duplicate check
        if (!string.IsNullOrWhiteSpace(entity.SKU))
        {
            var exists = await _repository.ExistsBySKUAsync(entity.SKU, entity.Id, cancellationToken);
            if (exists)
                errors.Add($"A product with SKU '{entity.SKU}' already exists");
        }

        return errors.Any()
            ? ValueResult<bool>.Failure(errors)
            : ValueResult<bool>.Success(true);
    }

    #endregion

    #region CRUD Operations

    protected override async Task<ValueResult<Product>> CreateInternalAsync(
        Product entity,
        CancellationToken cancellationToken = default)
    {
        await _repository.AddAsync(entity, cancellationToken);
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        return ValueResult<Product>.Success(entity);
    }

    protected override async Task<ValueResult<Product>> UpdateInternalAsync(
        Product entity,
        CancellationToken cancellationToken = default)
    {
        _repository.Update(entity);
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        return ValueResult<Product>.Success(entity);
    }

    public override async Task<Result> DeleteAsync(
        Product entity,
        CancellationToken cancellationToken = default)
    {
        _repository.Delete(entity);
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        return Result.Success();
    }

    public override async Task<Result> DeleteByIdAsync(
        Guid id,
        CancellationToken cancellationToken = default)
    {
        var product = await _repository.GetByIdAsync(id, cancellationToken);

        if (product == null)
            return Result.Failure($"Product with ID {id} not found");

        return await DeleteAsync(product, cancellationToken);
    }

    public override async Task<ValueResult<Product>> GetByIdAsync(
        Guid id,
        CancellationToken cancellationToken = default)
    {
        var product = await _repository.GetByIdAsync(id, cancellationToken);

        return product != null
            ? ValueResult<Product>.Success(product)
            : ValueResult<Product>.Failure($"Product with ID {id} not found");
    }

    public override async Task<ValueResult<IEnumerable<Product>>> GetAllAsync(
        CancellationToken cancellationToken = default)
    {
        var products = await _repository.GetAllAsync(cancellationToken);
        return ValueResult<IEnumerable<Product>>.Success(products);
    }

    public override async Task<ValueResult<IEnumerable<Product>>> FindAsync(
        Expression<Func<Product, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        var products = await _repository.FindAsync(predicate, cancellationToken);
        return ValueResult<IEnumerable<Product>>.Success(products);
    }

    #endregion

    #region Custom Methods

    public async Task<ValueResult<IEnumerable<Product>>> GetProductsInPriceRangeAsync(
        decimal minPrice,
        decimal maxPrice,
        CancellationToken cancellationToken = default)
    {
        if (minPrice < 0)
            return ValueResult<IEnumerable<Product>>.Failure("Minimum price cannot be negative");

        if (maxPrice < minPrice)
            return ValueResult<IEnumerable<Product>>.Failure("Maximum price must be greater than minimum price");

        var products = await _repository.GetProductsInPriceRangeAsync(minPrice, maxPrice, cancellationToken);
        return ValueResult<IEnumerable<Product>>.Success(products);
    }

    #endregion
}

Example 2: Multi-Entity Transaction

// YourApp.Application/Services/OrderService.cs
using IntraDotNet.CleanArchitecture.Application.Services;
using IntraDotNet.CleanArchitecture.Application.Results;
using YourApp.Application.Interfaces.Persistence;
using YourApp.Domain.Entities;

namespace YourApp.Application.Services;

public class OrderService : GuidValidatableDataService<Order>
{
    private readonly IOrderRepository _orderRepository;
    private readonly IProductRepository _productRepository;
    private readonly IUnitOfWork _unitOfWork;

    public OrderService(
        IOrderRepository orderRepository,
        IProductRepository productRepository,
        IUnitOfWork unitOfWork)
    {
        _orderRepository = orderRepository;
        _productRepository = productRepository;
        _unitOfWork = unitOfWork;
    }

    public async Task<ValueResult<Order>> CreateOrderWithStockUpdateAsync(
        Order order,
        CancellationToken cancellationToken = default)
    {
        // Validate the order
        var validationResult = await ValidateAsync(order, cancellationToken);
        if (validationResult.IsFailure)
            return ValueResult<Order>.Failure(validationResult.AggregateErrors!);

        // Start transaction
        await _unitOfWork.BeginTransactionAsync(cancellationToken);

        try
        {
            // 1. Create the order
            await _orderRepository.AddAsync(order, cancellationToken);

            // 2. Update stock for each product
            foreach (var item in order.Items)
            {
                var product = await _productRepository.GetByIdAsync(item.ProductId, cancellationToken);
                
                if (product == null)
                {
                    await _unitOfWork.RollbackTransactionAsync(cancellationToken);
                    return ValueResult<Order>.Failure($"Product with ID {item.ProductId} not found");
                }

                if (product.StockQuantity < item.Quantity)
                {
                    await _unitOfWork.RollbackTransactionAsync(cancellationToken);
                    return ValueResult<Order>.Failure($"Insufficient stock for product {product.Name}");
                }

                product.StockQuantity -= item.Quantity;
                _productRepository.Update(product);
            }

            // 3. Save all changes
            await _unitOfWork.SaveChangesAsync(cancellationToken);
            await _unitOfWork.CommitTransactionAsync(cancellationToken);

            return ValueResult<Order>.Success(order);
        }
        catch (Exception ex)
        {
            await _unitOfWork.RollbackTransactionAsync(cancellationToken);
            return ValueResult<Order>.Failure($"Failed to create order: {ex.Message}");
        }
    }

    public override async Task<ValueResult<bool>> ValidateAsync(
        Order entity,
        CancellationToken cancellationToken = default)
    {
        var errors = new List<string>();

        if (string.IsNullOrWhiteSpace(entity.OrderNumber))
            errors.Add("Order number is required");

        if (!entity.Items.Any())
            errors.Add("Order must contain at least one item");

        if (entity.TotalAmount <= 0)
            errors.Add("Order total must be greater than zero");

        return errors.Any()
            ? ValueResult<bool>.Failure(errors)
            : ValueResult<bool>.Success(true);
    }

    // ... implement required abstract methods ...
}

Example 3: Service Without Validation (Simple CRUD)

// YourApp.Application/Services/CategoryService.cs
using System.Linq.Expressions;
using IntraDotNet.CleanArchitecture.Application.Services;
using IntraDotNet.CleanArchitecture.Application.Results;
using YourApp.Application.Interfaces.Persistence;
using YourApp.Domain.Entities;

namespace YourApp.Application.Services;

/// <summary>
/// Simple service for Category entities without complex validation.
/// Uses IntDataService for integer-based identifiers.
/// </summary>
public class CategoryService : IntDataService<Category>
{
    private readonly ICategoryRepository _repository;
    private readonly IUnitOfWork _unitOfWork;

    public CategoryService(
        ICategoryRepository repository,
        IUnitOfWork unitOfWork)
    {
        _repository = repository;
        _unitOfWork = unitOfWork;
    }

    public override async Task<ValueResult<Category>> CreateAsync(
        Category entity,
        CancellationToken cancellationToken = default)
    {
        // Simple validation
        if (string.IsNullOrWhiteSpace(entity.Name))
            return ValueResult<Category>.Failure("Category name is required");

        await _repository.AddAsync(entity, cancellationToken);
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        return ValueResult<Category>.Success(entity);
    }

    public override async Task<ValueResult<Category>> UpdateAsync(
        Category entity,
        CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(entity.Name))
            return ValueResult<Category>.Failure("Category name is required");

        _repository.Update(entity);
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        return ValueResult<Category>.Success(entity);
    }

    public override async Task<Result> DeleteAsync(
        Category entity,
        CancellationToken cancellationToken = default)
    {
        _repository.Delete(entity);
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        return Result.Success();
    }

    public override async Task<Result> DeleteByIdAsync(
        int id,
        CancellationToken cancellationToken = default)
    {
        var category = await _repository.GetByIdAsync(id, cancellationToken);

        if (category == null)
            return Result.Failure($"Category with ID {id} not found");

        return await DeleteAsync(category, cancellationToken);
    }

    public override async Task<ValueResult<Category>> GetByIdAsync(
        int id,
        CancellationToken cancellationToken = default)
    {
        var category = await _repository.GetByIdAsync(id, cancellationToken);

        return category != null
            ? ValueResult<Category>.Success(category)
            : ValueResult<Category>.Failure($"Category with ID {id} not found");
    }

    public override async Task<ValueResult<IEnumerable<Category>>> GetAllAsync(
        CancellationToken cancellationToken = default)
    {
        var categories = await _repository.GetAllAsync(cancellationToken);
        return ValueResult<IEnumerable<Category>>.Success(categories);
    }

    public override async Task<ValueResult<IEnumerable<Category>>> FindAsync(
        Expression<Func<Category, bool>> predicate,
        CancellationToken cancellationToken = default)
    {
        var categories = await _repository.FindAsync(predicate, cancellationToken);
        return ValueResult<IEnumerable<Category>>.Success(categories);
    }
}

Best Practices

✅ Do's

  1. Use Result Pattern for All Public Methods

    // ✅ Good
    public async Task<ValueResult<Product>> GetProductAsync(Guid id)
    {
        var product = await _repository.GetByIdAsync(id);
        return product != null
            ? ValueResult<Product>.Success(product)
            : ValueResult<Product>.Failure("Product not found");
    }
    
  2. Validate in Services, Not Repositories

    // ✅ Good - Validation in service
    public async Task<ValueResult<Product>> CreateAsync(Product product)
    {
        var validationResult = await ValidateAsync(product);
        if (validationResult.IsFailure)
            return ValueResult<Product>.Failure(validationResult.AggregateErrors!);
    
        await _repository.AddAsync(product);
        return ValueResult<Product>.Success(product);
    }
    
  3. Use Unit of Work for Transactions

    // ✅ Good
    await _unitOfWork.BeginTransactionAsync();
    try
    {
        await _repository1.AddAsync(entity1);
        await _repository2.UpdateAsync(entity2);
        await _unitOfWork.SaveChangesAsync();
        await _unitOfWork.CommitTransactionAsync();
    }
    catch
    {
        await _unitOfWork.RollbackTransactionAsync();
        throw;
    }
    
  4. Keep Services Focused

    // ✅ Good - Single responsibility
    public class ProductService : GuidValidatableDataService<Product>
    {
        // Handles only Product-related operations
    }
    
    public class OrderService : GuidValidatableDataService<Order>
    {
        // Handles only Order-related operations
    }
    
  5. Define Repository Interfaces in Application Layer

    // ✅ Good - Interface in Application, Implementation in Infrastructure
    // YourApp.Application/Interfaces/Persistence/IProductRepository.cs
    public interface IProductRepository : IGuidRepository<Product>
    {
        Task<Product?> GetBySKUAsync(string sku);
    }
    

❌ Don'ts

  1. Don't Put Infrastructure Code in Application

    // ❌ Bad - DbContext in Application layer
    public class ProductService
    {
        private readonly DbContext _context; // NO!
    }
    
    // ✅ Good - Use repository interface
    public class ProductService
    {
        private readonly IProductRepository _repository; // YES!
    }
    
  2. Don't Swallow Exceptions

    // ❌ Bad
    try
    {
        await _repository.AddAsync(product);
    }
    catch
    {
        return null; // Lost the error!
    }
    
    // ✅ Good
    try
    {
        await _repository.AddAsync(product);
        return ValueResult<Product>.Success(product);
    }
    catch (Exception ex)
    {
        return ValueResult<Product>.Failure($"Failed to create product: {ex.Message}");
    }
    
  3. Don't Return Domain Entities Directly from API

    // ❌ Bad - Exposing domain entity to API
    [HttpGet]
    public async Task<Product> GetProduct(Guid id)
    {
        return await _service.GetByIdAsync(id);
    }
    
    // ✅ Good - Map to DTO in controller
    [HttpGet]
    public async Task<IActionResult> GetProduct(Guid id)
    {
        var result = await _service.GetByIdAsync(id);
        if (result.IsFailure)
            return NotFound(result.Error);
    
        var dto = MapToDto(result.Value);
        return Ok(dto);
    }
    
  4. Don't Mix Query and Command Logic

    // ❌ Bad - Doing too much in one method
    public async Task<Product> GetAndUpdateProductAsync(Guid id, decimal newPrice)
    {
        var product = await _repository.GetByIdAsync(id);
        product.Price = newPrice;
        await _repository.UpdateAsync(product);
        return product;
    }
    
    // ✅ Good - Separate concerns
    public async Task<ValueResult<Product>> GetProductAsync(Guid id) { /* ... */ }
    public async Task<ValueResult<Product>> UpdateProductPriceAsync(Guid id, decimal newPrice) { /* ... */ }
    

When to Use Which Service

Scenario Use
Simple CRUD with minimal validation GuidDataService or IntDataService
Complex business rules and validation GuidValidatableDataService or IntValidatableDataService
Need different DTOs for different operations Create custom service methods in derived class
Multi-entity transactions Inject multiple repositories and use IUnitOfWork
Read-only operations Consider creating separate query services/handlers

Integration with Other Layers

With Domain Layer

// Application service orchestrates domain entities
public async Task<ValueResult<Order>> PlaceOrderAsync(Order order)
{
    // Use domain methods
    order.CalculateTotal();
    order.Validate(); // Domain validation
    
    // Persist through repository
    await _repository.AddAsync(order);
    await _unitOfWork.SaveChangesAsync();
    
    return ValueResult<Order>.Success(order);
}

With Infrastructure Layer

// Application defines the contract
public interface IProductRepository : IGuidRepository<Product>
{
    Task<Product?> GetBySKUAsync(string sku);
}

// Infrastructure implements it
public class ProductRepository : GuidRepository<Product, AppDbContext>, IProductRepository
{
    public async Task<Product?> GetBySKUAsync(string sku)
    {
        return await Context.Products.FirstOrDefaultAsync(p => p.SKU == sku);
    }
}

With Presentation Layer

// Controller uses service through interface
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;
    
    [HttpPost]
    public async Task<IActionResult> Create(CreateProductDto dto)
    {
        var product = MapToEntity(dto);
        var result = await _productService.CreateAsync(product);
        
        if (result.IsFailure)
            return BadRequest(result.Error);
        
        return CreatedAtAction(nameof(Get), new { id = result.Value.Id }, MapToDto(result.Value));
    }
}

See Also

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on IntraDotNet.CleanArchitecture.Application:

Package Downloads
KeyZee

A minimal self-hostable encrypted key value pair sdk that can be used with any modern relational database written in .NET Core

IntraDotNet.CleanArchitecture.Infrastructure.EFCore

Entity Framework Core infrastructure implementation for Clean Architecture. Provides AuditableDbContext with automatic audit tracking, repository implementations, Unit of Work pattern, and soft delete support.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 175 12/31/2025

Initial release of IntraDotNet.CleanArchitecture.Application
     - IGuidRepository and IIntRepository interfaces
     - IUnitOfWork interface for transaction management
     - Result and ValueResult types for error handling
     - GuidDataService and IntDataService base implementations
     - GuidValidatableDataService and IntValidatableDataService with validation hooks
     - ICurrentUserService interface for user context