Skip to main content

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.

Aller à l'installation

Informations de source

Dépôt
abpframework/abp
Dernière activité de la source
22 mars 2026 à 17:13
Langue détectée de SKILL.md
anglais
Étoiles
14 434
Forks
3 718

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
abp-application-layer
description
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.
# 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 `GetAsync` not `GetBookAsync` - **ID inside UpdateDto**: pass `id` as a separate parameter, not inside the DTO - **Calling other app services in the same module**: use domain services or repositories directly - **Using `IFormFile`/`Stream` in app service**: accept `byte[]` from controllers instead - **Business logic in app service**: put it in domain entities or domain services ## Application Service Structure ### Interface (Application.Contracts) ```csharp 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) ```csharp 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 (`GetAsync` not `GetBookAsync`) - 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 `UpdateAsync` explicitly (don't assume change tracking) - Don't call other app services in same module - Don't use `IFormFile`/`Stream` - pass `byte[]` 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.Contracts` project - This allows sharing with clients (Blazor, HttpApi.Client) ## Validation ### Data Annotations ```csharp 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: ```csharp 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 ```csharp 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 ```csharp throw new BusinessException("BookStore:010001") .WithData("BookName", name); ``` ### Entity Not Found ```csharp var book = await _bookRepository.FindAsync(id); if (book == null) { throw new EntityNotFoundException(typeof(Book), id); } ``` ### User-Friendly Exceptions ```csharp 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: ```csharp [Mapper] public partial class BookMapper { public partial BookDto MapToDto(Book book); public partial List<BookDto> MapToDtoList(List<Book> books); } ``` Register in module: ```csharp public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddSingleton<BookMapper>(); } ``` Usage in application service: ```csharp 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.
Voir sur GitHub