Agent skill
abp-application-layer
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.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/abp-application-layer
SKILL.md
ABP Application Layer Patterns
Docs: https://abp.io/docs/latest/framework/architecture/domain-driven-design/application-services
Anti-Patterns to Avoid
- Entity name in method: use
GetAsyncnotGetBookAsync - ID inside UpdateDto: pass
idas a separate parameter, not inside the DTO - Calling other app services in the same module: use domain services or repositories directly
- Using
IFormFile/Streamin app service: acceptbyte[]from controllers instead - Business logic in app service: put it in domain entities or domain services
Application Service Structure
Interface (Application.Contracts)
public 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);
}
Implementation (Application)
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);
}
}
Application Service Best Practices
- Don't repeat entity name in method names (
GetAsyncnotGetBookAsync) - Accept/return DTOs only, never entities
- ID not inside UpdateDto - pass separately
- Use custom repositories when you need custom queries, generic repository is fine for simple CRUD
- Call
UpdateAsyncexplicitly (don't assume change tracking) - Don't call other app services in same module
- Don't use
IFormFile/Stream- passbyte[]from controllers - Use base class properties (
Clock,CurrentUser,GuidGenerator,L) instead of injecting these services
DTO Naming Conventions
| 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 |
DTO Location
- Define DTOs in
*.Application.Contractsproject - This allows sharing with clients (Blazor, HttpApi.Client)
Validation
Data Annotations
public class CreateBookDto
{
[Required]
[StringLength(100, MinimumLength = 3)]
public string Name { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
}
Custom Validation with IValidatableObject
Before adding custom validation, decide if it's a domain rule or application rule:
- Domain rule: Put validation in entity constructor or domain service (enforces business invariants)
- Application rule: Use DTO validation (input format, required fields)
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) }
);
}
}
}
FluentValidation
public class CreateBookDtoValidator : AbstractValidator<CreateBookDto>
{
public CreateBookDtoValidator()
{
RuleFor(x => x.Name).NotEmpty().Length(3, 100);
RuleFor(x => x.Price).GreaterThan(0);
}
}
Error Handling
Business Exceptions
throw new BusinessException("BookStore:010001")
.WithData("BookName", name);
Entity Not Found
var book = await _bookRepository.FindAsync(id);
if (book == null)
{
throw new EntityNotFoundException(typeof(Book), id);
}
User-Friendly Exceptions
throw new UserFriendlyException(L["BookNotAvailable"]);
HTTP Status Code Mapping
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 |
Auto API Controllers
ABP automatically generates API controllers for application services:
- Interface must inherit
IApplicationService(which already has[RemoteService]attribute) - HTTP methods determined by method name prefix (Get, Create, Update, Delete)
- Use
[RemoteService(false)]to disable auto API generation for specific methods
Object Mapping (Mapperly / AutoMapper)
ABP supports both Mapperly and AutoMapper integrations. But the default mapping library is Mapperly. You need to first check the project's active mapping library.
- Prefer the mapping provider already used in the solution (check existing mapping files / loaded modules).
- In mixed solutions, explicitly setting the default provider may be required (see
docs/en/release-info/migration-guides/AutoMapper-To-Mapperly.md).
Mapperly (compile-time)
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.
AutoMapper (runtime)
If the solution uses AutoMapper, mappings are typically defined in Profile classes and registered via ABP's AutoMapper integration.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?