Services Overview¶
This section provides comprehensive documentation for all application services in the Cargonerds solution.
What are Application Services?¶
Application services are the entry points to the application's business logic. They:
- Implement use cases and coordinate domain objects
- Work with repositories and domain services
- Return Data Transfer Objects (DTOs) to the presentation layer
- Handle authorization and validation
- Manage transactions through Unit of Work
Service Organization¶
Services in Cargonerds are organized by module and business domain:
Cargonerds Core Services¶
Located in src/Cargonerds.Application/:
| Service | Description | Documentation |
|---|---|---|
| BookAppService | Manages books (sample domain) | Book Service |
| ClientSettingsAppService | Manages client-specific settings | Internal |
| CurrentOrganizationAppService | Provides current organization context | Internal |
| OrganizationUnitAppService | Manages organization units | Internal |
Hub Module Services¶
Located in modules/hub/src/Hub.Application/Services/:
| Service | Description | Documentation |
|---|---|---|
| ShipmentAppService | Manages shipments and logistics | Shipment Service |
| HubOrganizationAppService | Manages organizations in Hub | Organization Service |
| DocumentAppService | Manages shipment documents | Document Service |
| OrderAppService | Manages orders | Internal |
| ProfileAppService | Manages user profiles | Internal |
| TagAppService | Manages tags for categorization | Internal |
Common Service Patterns¶
CRUD Operations¶
Most services follow a standard CRUD pattern:
public interface IBookAppService : IApplicationService
{
Task<BookDto> GetAsync(Guid id);
Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input);
Task<BookDto> CreateAsync(CreateUpdateBookDto input);
Task<BookDto> UpdateAsync(Guid id, CreateUpdateBookDto input);
Task DeleteAsync(Guid id);
}
Pagination¶
Services use ABP's pagination DTOs:
public class PagedAndSortedResultRequestDto
{
public int SkipCount { get; set; } // Number of records to skip
public int MaxResultCount { get; set; } // Max records to return
public string Sorting { get; set; } // Sort expression
}
public class PagedResultDto<T>
{
public long TotalCount { get; set; }
public List<T> Items { get; set; }
}
Filtering¶
Hub services support advanced filtering:
public class PagedAndSortedResultRequestWithFacetOptionsDto : PagedAndSortedResultRequestDto
{
public Dictionary<string, List<string>> Filters { get; set; }
public bool IncludeFacets { get; set; }
}
public class PagedResultDtoWithFilters<T> : PagedResultDto<T>
{
public List<FacetResult> Facets { get; set; }
public List<AppliedFilter> AppliedFilters { get; set; }
}
Authorization¶
All services require appropriate permissions:
[Authorize(CargonerdsPermissions.Books.Default)]
public class BookAppService : ApplicationService
{
[Authorize(CargonerdsPermissions.Books.Create)]
public async Task<BookDto> CreateAsync(CreateUpdateBookDto input)
{
// Implementation
}
}
Base Service Classes¶
ApplicationService¶
Base class for all application services:
public abstract class ApplicationService : IApplicationService
{
// Provides access to:
// - ObjectMapper (for DTO mapping)
// - CurrentUser (current user info)
// - CurrentTenant (current tenant info)
// - Logger (for logging)
// - LazyServiceProvider (for lazy injection)
// - L (for localization)
}
HubEntityBaseService¶
Custom base service for Hub entities:
public abstract class HubEntityBaseService<TEntity, TDto> : ApplicationService
where TEntity : class, IEntity
{
// Provides:
// - Common CRUD operations
// - Pagination support
// - Filtering and faceting
// - Organization context
// - Permission checking
protected abstract TDto? ToDto(TEntity? entity);
protected abstract string ReadPermission { get; }
protected virtual Task<IQueryable<TEntity>> WithDetailsAsync();
}
Error Handling¶
Services use ABP's exception handling:
// User-friendly errors
throw new UserFriendlyException("Book not found");
// Business rule violations
throw new BusinessException("Books:DuplicateISBN")
.WithData("ISBN", input.ISBN);
// Authorization errors (handled by ABP)
[Authorize(CargonerdsPermissions.Books.Create)]
Validation¶
DTOs are automatically validated:
public class CreateUpdateBookDto
{
[Required]
[StringLength(128)]
public string Name { get; set; }
[Required]
public BookType Type { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime PublishDate { get; set; }
[Required]
[Range(0, 999.99)]
public float Price { get; set; }
}
Unit of Work¶
All service methods are automatically wrapped in a Unit of Work:
[UnitOfWork]
public virtual async Task CreateBookWithRelationsAsync(CreateBookDto input)
{
// All operations in one transaction
var author = await _authorRepository.InsertAsync(...);
var book = await _bookRepository.InsertAsync(...);
var category = await _categoryRepository.InsertAsync(...);
// Auto-committed if no exception
}
Object Mapping¶
Services use AutoMapper through ObjectMapper:
public async Task<BookDto> GetAsync(Guid id)
{
var book = await _repository.GetAsync(id);
// Map entity to DTO
return ObjectMapper.Map<Book, BookDto>(book);
}
public async Task<BookDto> CreateAsync(CreateUpdateBookDto input)
{
// Map DTO to entity
var book = ObjectMapper.Map<CreateUpdateBookDto, Book>(input);
await _repository.InsertAsync(book);
return ObjectMapper.Map<Book, BookDto>(book);
}
Localization¶
Services can use localized strings:
public async Task ValidateAsync(Book book)
{
if (book.Price < 0)
{
throw new UserFriendlyException(
L["NegativePriceNotAllowed"]
);
}
}
Testing Services¶
Application services can be easily tested:
public class BookAppService_Tests : CargonerdsApplicationTestBase
{
private readonly IBookAppService _bookAppService;
public BookAppService_Tests()
{
_bookAppService = GetRequiredService<IBookAppService>();
}
[Fact]
public async Task Should_Get_Book()
{
var book = await _bookAppService.CreateAsync(new CreateUpdateBookDto
{
Name = "Test Book",
Type = BookType.ScienceFiction,
PublishDate = DateTime.Now,
Price = 19.99f
});
var result = await _bookAppService.GetAsync(book.Id);
result.ShouldNotBeNull();
result.Name.ShouldBe("Test Book");
}
}
API Exposure¶
Application services are automatically exposed as REST APIs:
GET /api/app/book/{id}
GET /api/app/book
POST /api/app/book
PUT /api/app/book/{id}
DELETE /api/app/book/{id}
Best Practices¶
- One Service Per Aggregate: Create one application service for each aggregate root
- Use DTOs: Never expose domain entities directly
- Authorization First: Always protect operations with permissions
- Keep Methods Focused: Each method should implement one use case
- Handle Errors Gracefully: Use appropriate exception types
- Log Important Operations: Use the built-in logger
- Consider Performance: Use pagination for large datasets
- Test Thoroughly: Write unit tests for business logic
Service Lifecycle¶
graph TD
A[HTTP Request] --> B[Controller/API Endpoint]
B --> C[Application Service]
C --> D[Authorization Check]
D --> E[DTO Validation]
E --> F[Begin Unit of Work]
F --> G[Repository Operations]
G --> H[Domain Logic]
H --> I[Commit Transaction]
I --> J[Map to DTO]
J --> K[Return Response]
Next Steps¶
Explore detailed documentation for specific services:
- Book Service - Sample CRUD service
- Shipment Service - Complex business operations
- Organization Service - Multi-tenant operations
- Document Service - File management
For implementation details, see: - Application Services Guide - Authorization - Data Transfer Objects