name: ABP Framework Expert description: Best practices and conventions for developing with ABP Framework 9.x, based on official ABP.IO documentation and DDD principles.
ABP Framework Expert Skill
This skill guides the agent to follow official ABP Framework best practices when modifying or extending this project.
Official References
- Module Architecture
- Entity Best Practices
- Repository Best Practices
- Domain Services Best Practices
- Application Services Best Practices
- DTO Best Practices
- EF Core Integration
Project Context
| Component | Technology | Location |
|---|---|---|
| Backend | ABP Framework 9.0.4 (.NET 9) | src/ |
| Admin Frontend | Blazor WebApp | src/Knowledtree.Blazor.* |
| Customer Frontend | React Native + TypeScript | mobile/ |
| Database | PostgreSQL via EF Core | src/Knowledtree.EntityFrameworkCore/ |
| DB Table Prefix | App |
KnowledtreeConsts.DbTablePrefix |
Solution Layer Map
When creating or modifying code, always place files in the correct project/layer:
| Layer | Project | What goes here |
|---|---|---|
| Domain.Shared | Knowledtree.Domain.Shared |
Enums, constants, error codes, localization |
| Domain | Knowledtree.Domain |
Entities, Aggregate Roots, Repository interfaces, Domain Services (Managers) |
| Application.Contracts | Knowledtree.Application.Contracts |
AppService interfaces (I*AppService), DTOs, Permissions |
| Application | Knowledtree.Application |
AppService implementations, AutoMapper profiles |
| EntityFrameworkCore | Knowledtree.EntityFrameworkCore |
DbContext, Migrations, Repository implementations |
| HttpApi | Knowledtree.HttpApi |
Custom Controllers (only if auto API is not sufficient) |
| Web | Knowledtree.Web / Knowledtree.Blazor.* |
Pages, Menus, UI components |
Best Practices by Building Block
1. Entities & Aggregate Roots
Rules:
- Use
Guidas primary key for all aggregate roots. No composite keys. - Inherit from
AggregateRoot<Guid>or audited variants (FullAuditedAggregateRoot<Guid>, etc.). - Define a primary constructor (public/internal) that validates required fields. Use
Check.NotNullOrWhiteSpace(). - Define a protected parameterless constructor for ORM compatibility.
- Make all properties and methods
virtual. - Use private/protected setters for properties that need consistency protection. Provide public setter methods (e.g.,
SetTitle()). - Reference other aggregate roots only by
GuidId, never by navigation property. - Initialize sub-collections in the primary constructor.
- Keep aggregates small.
Example: See examples/Entity_AggregateRoot.cs
2. Repositories
Rules:
- Define repository interfaces in the Domain layer for each aggregate root.
- Inherit interface from
IBasicRepository<TEntity, TKey>(NOTIRepository<>— it exposesIQueryable). - Do NOT define repositories for non-aggregate-root entities.
- Implement repositories in the EntityFrameworkCore layer, inheriting
EfCoreRepository<TDbContext, TEntity, TKey>. - Use the DbContext interface (not class) as the generic parameter.
- Pass
cancellationTokenusingGetCancellationToken()helper. - Create
IncludeDetailsextension methods for aggregates with sub-collections.
Example: See examples/Repository.cs
3. Domain Services (Managers)
Rules:
- Define in the Domain layer.
- Name with
Managersuffix (e.g.,IssueManager). - Inherit from
DomainService. - Do NOT create interfaces unless needed for testing/mocking.
- Do NOT define GET methods — use repositories directly for reads.
- Only define methods that mutate state.
- Use self-explanatory method names (e.g.,
AssignToAsync, notUpdateAsync). - Accept domain objects as parameters, not DTOs.
- Return domain objects only, never DTOs.
- Throw
BusinessExceptionfor validation failures. - Do NOT access
CurrentUser— pass user data from the Application layer.
4. Application Services
Rules:
- Create one AppService per aggregate root.
- Define interface in
Application.ContractsinheritingIApplicationService. - Use
AppServicepostfix (e.g.,IBookAppService/BookAppService). - Inherit implementation from
KnowledtreeAppService(project base class). - All public methods must be
virtual. Useprotected virtualinstead ofprivate. - Use DTOs for all inputs/outputs — never return entities.
- Use specifically designed repositories (e.g.,
IBookRepository), not genericIRepository<Book>. - Do NOT write LINQ/SQL queries in AppService — delegate to repository.
- Do NOT call other AppServices from the same module. Use domain layer or extract shared logic.
- For file handling: accept
byte[], notIFormFileorStream.
Example: See examples/AppService.cs
5. DTOs
Rules:
- Define in
Application.Contracts. - Inherit from base DTO classes:
EntityDto<Guid>,AuditedEntityDto<Guid>,FullAuditedEntityDto<Guid>, etc. - For aggregate root DTOs, use extensible variants:
ExtensibleAuditedEntityDto<Guid>. - Use public getters and setters.
- Use data annotations for input validation (
[Required],[StringLength], etc.). - Do NOT add logic to DTOs (except
IValidatableObjectwhen needed). - Mark DTOs as
[Serializable].
Example: See examples/Dto.cs
6. EF Core Integration
Rules:
- Add
DbSet<TEntity>properties toKnowledtreeDbContextonly for aggregate roots. - Configure entity mapping in
OnModelCreatingusingConfigureByConvention(). - Use
KnowledtreeConsts.DbTablePrefix("App") + entity name for table names. - Set schema to
KnowledtreeConsts.DbSchema(null). - Do NOT enable lazy loading.
Example (in DbContext.OnModelCreating):
builder.Entity<Book>(b =>
{
b.ToTable(KnowledtreeConsts.DbTablePrefix + "Books", KnowledtreeConsts.DbSchema);
b.ConfigureByConvention();
// Additional configuration...
});
Step-by-Step: Add a New Entity (End-to-End)
When asked to create a new entity (e.g., "Book"), follow these steps in order:
Step 1: Domain.Shared
- Add any enums or constants related to the entity.
- Add max length constants (e.g.,
BookConsts.MaxTitleLength).
Step 2: Domain
- Create the Entity/AggregateRoot class (see example).
- Create the Repository interface (
IBookRepository : IBasicRepository<Book, Guid>). - (Optional) Create a Domain Service if complex business logic is needed.
Step 3: Application.Contracts
- Create DTOs:
BookDto,CreateUpdateBookDto. - Create the AppService interface:
IBookAppService. - Add Permissions if needed.
Step 4: Application
- Implement
BookAppService. - Add AutoMapper mappings in
KnowledtreeApplicationAutoMapperProfile.
Step 5: EntityFrameworkCore
- Add
DbSet<Book>toKnowledtreeDbContext. - Configure entity mapping in
OnModelCreating. - (Optional) Implement custom repository:
EfCoreBookRepository. - Create a new EF Core migration.
Step 6: HttpApi (usually automatic)
- ABP auto-generates API controllers from AppServices. Only create a manual controller if you need custom routing or file upload endpoints.
Step 7: Migration
cd src/Knowledtree.EntityFrameworkCore
dotnet ef migrations add Added_Book
Then run Knowledtree.DbMigrator project to apply.