.agents/skills/abp-application-layer/SKILL.md
ABP Application Services, DTOs, CRUD service, object mapping (Mapperly/AutoMapper), validation, error handling. Use when creating or reviewing application services, DTOs, or working in the Application or Application.Contracts projects.
npx skillsauth add afonsoft/VideoChat abp-application-layerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Docs: https://abp.io/docs/latest/framework/architecture/domain-driven-design/application-services
GetAsync not GetBookAsyncid as a separate parameter, not inside the DTOIFormFile/Stream in app service: accept byte[] from controllers insteadpublic interface IBookAppService : IApplicationService
{
Task<BookDto> GetAsync(Guid id);
Task<PagedResultDto<BookListItemDto>> GetListAsync(GetBookListInput input);
Task<BookDto> CreateAsync(CreateBookDto input);
Task<BookDto> UpdateAsync(Guid id, UpdateBookDto input);
Task DeleteAsync(Guid id);
}
public class BookAppService : ApplicationService, IBookAppService
{
private readonly IBookRepository _bookRepository;
private readonly BookManager _bookManager;
private readonly BookMapper _bookMapper;
public BookAppService(
IBookRepository bookRepository,
BookManager bookManager,
BookMapper bookMapper)
{
_bookRepository = bookRepository;
_bookManager = bookManager;
_bookMapper = bookMapper;
}
public async Task<BookDto> GetAsync(Guid id)
{
var book = await _bookRepository.GetAsync(id);
return _bookMapper.MapToDto(book);
}
[Authorize(BookStorePermissions.Books.Create)]
public async Task<BookDto> CreateAsync(CreateBookDto input)
{
var book = await _bookManager.CreateAsync(input.Name, input.Price);
await _bookRepository.InsertAsync(book);
return _bookMapper.MapToDto(book);
}
[Authorize(BookStorePermissions.Books.Edit)]
public async Task<BookDto> UpdateAsync(Guid id, UpdateBookDto input)
{
var book = await _bookRepository.GetAsync(id);
await _bookManager.ChangeNameAsync(book, input.Name);
book.SetPrice(input.Price);
await _bookRepository.UpdateAsync(book);
return _bookMapper.MapToDto(book);
}
}
GetAsync not GetBookAsync)UpdateAsync explicitly (don't assume change tracking)IFormFile/Stream - pass byte[] from controllersClock, CurrentUser, GuidGenerator, L) instead of injecting these services| Purpose | Convention | Example |
|---------|------------|---------|
| Query input | Get{Entity}Input | GetBookInput |
| List query input | Get{Entity}ListInput | GetBookListInput |
| Create input | Create{Entity}Dto | CreateBookDto |
| Update input | Update{Entity}Dto | UpdateBookDto |
| Single entity output | {Entity}Dto | BookDto |
| List item output | {Entity}ListItemDto | BookListItemDto |
*.Application.Contracts projectpublic class CreateBookDto
{
[Required]
[StringLength(100, MinimumLength = 3)]
public string Name { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
}
Before adding custom validation, decide if it's a domain rule or application rule:
Only use IValidatableObject for application-level validation that can't be expressed with data annotations:
public class CreateBookDto : IValidatableObject
{
public string Name { get; set; }
public string Description { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Name == Description)
{
yield return new ValidationResult(
"Name and Description cannot be the same!",
new[] { nameof(Name), nameof(Description) }
);
}
}
}
public class CreateBookDtoValidator : AbstractValidator<CreateBookDto>
{
public CreateBookDtoValidator()
{
RuleFor(x => x.Name).NotEmpty().Length(3, 100);
RuleFor(x => x.Price).GreaterThan(0);
}
}
throw new BusinessException("BookStore:010001")
.WithData("BookName", name);
var book = await _bookRepository.FindAsync(id);
if (book == null)
{
throw new EntityNotFoundException(typeof(Book), id);
}
throw new UserFriendlyException(L["BookNotAvailable"]);
Status code mapping is configurable in ABP (do not rely on a fixed mapping in business logic).
| Exception | Typical HTTP Status |
|-----------|-------------|
| AbpValidationException | 400 |
| AbpAuthorizationException | 401/403 |
| EntityNotFoundException | 404 |
| BusinessException | 403 (but configurable) |
| Other exceptions | 500 |
ABP automatically generates API controllers for application services:
IApplicationService (which already has [RemoteService] attribute)[RemoteService(false)] to disable auto API generation for specific methodsABP supports both Mapperly and AutoMapper integrations. But the default mapping library is Mapperly. You need to first check the project's active mapping library.
docs/en/release-info/migration-guides/AutoMapper-To-Mapperly.md).Define mappers as partial classes:
[Mapper]
public partial class BookMapper
{
public partial BookDto MapToDto(Book book);
public partial List<BookDto> MapToDtoList(List<Book> books);
}
Register in module:
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddSingleton<BookMapper>();
}
Usage in application service:
public class BookAppService : ApplicationService
{
private readonly BookMapper _bookMapper;
public BookAppService(BookMapper bookMapper)
{
_bookMapper = bookMapper;
}
public BookDto GetBook(Book book)
{
return _bookMapper.MapToDto(book);
}
}
Note: Mapperly generates mapping code at compile-time, providing better performance than runtime mappers.
If the solution uses AutoMapper, mappings are typically defined in Profile classes and registered via ABP's AutoMapper integration.
development
This skill enables visual inspection of websites running locally or remotely to identify and fix design issues. Triggers on requests like "review website design", "check the UI", "fix the layout", "find design problems". Detects issues with responsive design, accessibility, visual consistency, and layout breakage, then performs fixes at the source code level.
testing
Comprehensive unit testing with xUnit, mocking, test patterns, and best practices for .NET applications
data-ai
Universal SQL performance optimization assistant for comprehensive query tuning, indexing strategies, and database performance analysis across all SQL databases (MySQL, PostgreSQL, SQL Server, Oracle). Provides execution plan analysis, pagination optimization, batch operations, and performance monitoring guidance.
development
Universal SQL code review assistant that performs comprehensive security, maintainability, and code quality analysis across all SQL databases (MySQL, PostgreSQL, SQL Server, Oracle). Focuses on SQL injection prevention, access control, code standards, and anti-pattern detection. Complements SQL optimization prompt for complete development coverage.