ABP Core Conventions
Documentation: https://abp.io/docs/latest API Reference: https://abp.io/docs/api/
Key Rules
- Use
IClock/Clock.Nowinstead ofDateTime.Now/DateTime.UtcNow - Use
ITransientDependency/ISingletonDependencyinstead ofAddScoped/AddTransient/AddSingleton - Use
IRepository<T>instead of injectingDbContextdirectly - Check base class properties (
Clock,CurrentUser,GuidGenerator,L) before injecting services - Use
BusinessExceptionwith namespaced error codes for domain rule violations
Module System
Every ABP application/module has a module class that configures services:
[DependsOn(
typeof(AbpDddDomainModule),
typeof(AbpEntityFrameworkCoreModule)
)]
public class MyAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Service registration and configuration
}
}
Note: Middleware configuration (
OnApplicationInitialization) should only be done in the final host application, not in reusable modules.
Dependency Injection Conventions
Automatic Registration
ABP automatically registers services implementing marker interfaces:
ITransientDependency→ Transient lifetimeISingletonDependency→ Singleton lifetimeIScopedDependency→ Scoped lifetime
Classes inheriting from ApplicationService, DomainService, AbpController are also auto-registered.
Repository Usage
You can use the generic IRepository<TEntity, TKey> for simple CRUD operations. Define custom repository interfaces only when you need custom query methods:
// Simple CRUD - Generic repository is fine
public class BookAppService : ApplicationService
{
private readonly IRepository<Book, Guid> _bookRepository; // ✅ OK for simple operations
}
// Custom queries needed - Define custom interface
public interface IBookRepository : IRepository<Book, Guid>
{
Task<Book> FindByNameAsync(string name); // Custom query
}
public class BookAppService : ApplicationService
{
private readonly IBookRepository _bookRepository; // ✅ Use custom when needed
}
Exposing Services
[ExposeServices(typeof(IMyService))]
public class MyService : IMyService, ITransientDependency { }
Important Base Classes
| Base Class | Purpose |
|---|---|
Entity<TKey> |
Basic entity with ID |
AggregateRoot<TKey> |
DDD aggregate root |
DomainService |
Domain business logic |
ApplicationService |
Use case orchestration |
AbpController |
REST API controller |
ABP base classes already inject commonly used services as properties. Before injecting a service, check if it's already available:
| Property | Available In | Description |
|---|---|---|
GuidGenerator |
All base classes | Generate GUIDs |
Clock |
All base classes | Current time (use instead of DateTime) |
CurrentUser |
All base classes | Authenticated user info |
CurrentTenant |
All base classes | Multi-tenancy context |
L (StringLocalizer) |
ApplicationService, AbpController |
Localization |
AuthorizationService |
ApplicationService, AbpController |
Permission checks |
FeatureChecker |
ApplicationService, AbpController |
Feature availability |
DataFilter |
All base classes | Data filtering (soft-delete, tenant) |
UnitOfWorkManager |
ApplicationService, DomainService |
Unit of work management |
LoggerFactory |
All base classes | Create loggers |
Logger |
All base classes | Logging (auto-created) |
LazyServiceProvider |
All base classes | Lazy service resolution |
Useful methods from base classes:
CheckPolicyAsync()- Check permission and throw if not grantedIsGrantedAsync()- Check permission without throwing
Async Best Practices
- Use async all the way - never use
.Resultor.Wait() - All async methods should end with
Asyncsuffix - ABP automatically handles
CancellationTokenin most cases (e.g., fromHttpContext.RequestAborted) - Only pass
CancellationTokenexplicitly when implementing custom cancellation logic
Time Handling
Never use DateTime.Now or DateTime.UtcNow directly. Use ABP's IClock service:
// In classes inheriting from base classes (ApplicationService, DomainService, etc.)
public class BookAppService : ApplicationService
{
public void DoSomething()
{
var now = Clock.Now; // ✅ Already available as property
}
}
// In other services - inject IClock
public class MyService : ITransientDependency
{
private readonly IClock _clock;
public MyService(IClock clock) => _clock = clock;
public void DoSomething()
{
var now = _clock.Now; // ✅ Correct
// var now = DateTime.Now; // ❌ Wrong - not testable, ignores timezone settings
}
}
Tip: Before injecting a service, check if it's already available as a property in your base classes.
Business Exceptions
Use BusinessException for domain rule violations with namespaced error codes:
throw new BusinessException("MyModule:BookNameAlreadyExists")
.WithData("Name", bookName);
Configure localization mapping:
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("MyModule", typeof(MyModuleResource));
});
Localization
- In base classes (
ApplicationService,AbpController, etc.): UseL["Key"]- this is theIStringLocalizerproperty - In other services: Inject
IStringLocalizer<TResource> - Always localize user-facing messages and exceptions
Localization file location: *.Domain.Shared/Localization/{ResourceName}/{lang}.json
// Example: MyProject.Domain.Shared/Localization/MyProject/en.json
{
"culture": "en",
"texts": {
"Menu:Home": "Home",
"Welcome": "Welcome",
"BookName": "Book Name"
}
}
❌ Never Use (ABP Anti-Patterns)
| Don't Use | Use Instead |
|---|---|
| Minimal APIs | ABP Controllers or Auto API Controllers |
| MediatR | Application Services |
DbContext directly in App Services |
IRepository<T> |
AddScoped/AddTransient/AddSingleton |
ITransientDependency, ISingletonDependency |
DateTime.Now |
IClock / Clock.Now |
| Custom UnitOfWork | ABP's IUnitOfWorkManager |
| Manual HTTP calls from UI | ABP client proxies (generate-proxy) |
| Hardcoded role checks | Permission-based authorization |
| Business logic in Controllers | Application Services |