Browse Source

added AI guidelines for ABP Framework itself for Cursor and Copilot

pull/24563/head
İsmail ÇAĞDAŞ 1 month ago
parent
commit
adab0ae387
  1. 82
      .cursorrules
  2. 372
      .github/copilot-instructions.md

82
.cursorrules

@ -1,7 +1,7 @@
# ABP Framework – Cursor Rules
# Scope: ABP Framework repository (abpframework/abp). Apply to framework + module development.
# Goal: Enforce ABP module architecture best practices (DDD, layering, DB/ORM independence)
# and align contributions with the ABP contribution guide.
# Scope: ABP Framework repository (abpframework/abp) — for developing ABP itself, not ABP-based applications.
# Goal: Enforce ABP module architecture best practices (DDD, layering, DB/ORM independence),
# maintain backward compatibility, ensure extensibility, and align with ABP contribution guidelines.
## Global Defaults
- Follow existing patterns in this repository first. Before generating new code, search for similar implementations and mirror their structure, naming, and conventions.
@ -50,10 +50,10 @@
## Repositories
- Define repository interfaces in the domain layer.
- Create one dedicated repository interface per aggregate root (e.g., `IProductRepository`).
- Application code MUST NOT use generic repositories (e.g., `IRepository<TEntity, TKey>`).
- Repository interfaces MUST NOT expose `IQueryable` to application/domain layers:
- Prefer inheriting from `IBasicRepository<TEntity, TKey>` (or `IReadOnlyRepository<...>` when suitable).
- Do NOT inherit from `IRepository<TEntity, TKey>`.
- Public repository interfaces exposed by modules:
- SHOULD inherit from `IBasicRepository<TEntity, TKey>` (or `IReadOnlyRepository<...>` when suitable).
- SHOULD NOT expose `IQueryable` in the public contract.
- Internal implementations MAY use `IRepository<TEntity, TKey>` and `IQueryable` as needed.
- Do NOT define repositories for non-aggregate-root entities.
- Repository method conventions:
- All methods async.
@ -68,7 +68,8 @@
- Default: do NOT create interfaces for domain services unless necessary (mocking/multiple implementations).
- Naming: use `*Manager` suffix.
- Domain service methods:
- Do NOT define read-only “GET” methods. Querying belongs to repositories and application services.
- Focus on operations that enforce domain invariants and business rules.
- Query methods are acceptable when they encapsulate domain-specific lookup logic (e.g., normalized lookups, caching, complex resolution). Simple queries belong in repositories.
- Define methods that mutate state and enforce domain rules.
- Use specific, intention-revealing names (avoid generic `UpdateXAsync`).
- Accept valid domain objects as parameters; do NOT accept/return DTOs.
@ -135,7 +136,7 @@
- Use data annotations.
- Reuse constants from Domain.Shared wherever possible.
- Avoid logic in DTOs; only implement `IValidatableObject` when necessary.
- Mark DTOs `[Serializable]`.
- Do NOT use `[Serializable]` attribute (BinaryFormatter is obsolete); ABP uses JSON serialization.
- Output DTO strategy:
- Prefer a Basic DTO and a Detailed DTO; avoid many variants.
- Detailed DTOs: include reference details as nested basic DTOs; avoid duplicating raw FK ids unnecessarily.
@ -186,6 +187,69 @@
- Ignore `includeDetails` for MongoDB in most cases (documents load sub-collections).
- Prefer `GetQueryableAsync()` to ensure ABP data filters are applied.
## ABP Module Classes
- Every package must have exactly one `AbpModule` class.
- Naming: `Abp[ModuleName][Layer]Module` (e.g., `AbpIdentityDomainModule`, `AbpIdentityApplicationModule`).
- Use `[DependsOn(typeof(...))]` to declare module dependencies explicitly.
- Override `ConfigureServices` for DI registration and configuration.
- Override `OnApplicationInitialization` sparingly; prefer `ConfigureServices` when possible.
- Each module must be usable standalone; avoid hidden cross-module coupling.
## Framework Extensibility
- All public and protected members should be `virtual` for inheritance-based extensibility.
- Prefer `protected virtual` over `private` for helper methods to allow overriding.
- Use `[Dependency(ReplaceServices = true)]` patterns for services intended to be replaceable.
- Provide extension points via interfaces and virtual methods.
- Document extension points with XML comments explaining intended usage.
- Consider providing `*Options` classes for configuration-based extensibility.
## Backward Compatibility
- Do NOT remove or rename public API members without a deprecation cycle.
- Use `[Obsolete("Message. Use X instead.")]` with clear migration guidance before removal.
- Maintain binary and source compatibility within major versions.
- Add new optional parameters with defaults; do not change existing method signatures.
- When adding new abstract members to base classes, provide default implementations if possible.
- Prefer adding new interfaces over modifying existing ones.
## Localization Resources
- Define localization resources in Domain.Shared.
- Resource class naming: `[ModuleName]Resource` (e.g., `IdentityResource`, `PermissionManagementResource`).
- JSON files under `/Localization/[ModuleName]/` directory.
- Use `LocalizableString.Create<TResource>("Key")` for localizable exceptions and messages.
- All user-facing strings must be localized; no hardcoded English text in code.
- Error codes should be namespaced: `ModuleName:ErrorCode` (e.g., `Identity:UserNameAlreadyExists`).
## Settings & Features
- Define settings in `*SettingDefinitionProvider` in Domain.Shared or Domain.
- Setting names must follow `Abp.[ModuleName].[SettingName]` convention.
- Define features in `*FeatureDefinitionProvider` in Domain.Shared.
- Feature names must follow `[ModuleName].[FeatureName]` convention.
- Use constants for setting/feature names; never hardcode strings.
## Permissions
- Define permissions in `*PermissionDefinitionProvider` in Application.Contracts.
- Permission names must follow `[ModuleName].[Permission]` convention.
- Use constants for permission names (e.g., `IdentityPermissions.Users.Create`).
- Group related permissions logically.
## Event Bus & Distributed Events
- Use `ILocalEventBus` for intra-module communication within the same process.
- Use `IDistributedEventBus` for cross-module or cross-service communication.
- Define Event Transfer Objects (ETOs) in Domain.Shared for distributed events.
- ETO naming: `[EntityName][Action]Eto` (e.g., `UserCreatedEto`, `OrderCompletedEto`).
- Event handlers belong in the Application layer.
- ETOs should be simple, serializable, and contain only primitive types or nested ETOs.
## Testing
- Unit tests: `*.Tests` projects for isolated logic testing with mocked dependencies.
- Integration tests: `*.EntityFrameworkCore.Tests` / `*.MongoDB.Tests` for repository and DB tests.
- Use `AbpIntegratedTest<TModule>` or `AbpApplicationTestBase<TModule>` base classes.
- Test modules should use `[DependsOn]` on the module under test.
- Use `Shouldly` assertions (ABP convention).
- Test both EF Core and MongoDB implementations when the module supports both.
- Include tests for permission checks, validation, and edge cases.
- Name test methods: `MethodName_Scenario_ExpectedResult` or `Should_ExpectedBehavior_When_Condition`.
## Contribution Discipline (PR / Issues / Tests)
- Before significant changes, align via GitHub issue/discussion.
- PRs:

372
.github/copilot-instructions.md

@ -0,0 +1,372 @@
# ABP Framework – GitHub Copilot Instructions
> **Scope**: ABP Framework repository (abpframework/abp) — for developing ABP itself, not ABP-based applications.
>
> **Goal**: Enforce ABP module architecture best practices (DDD, layering, DB/ORM independence), maintain backward compatibility, ensure extensibility, and align with ABP contribution guidelines.
---
## Global Defaults
- Follow existing patterns in this repository first. Before generating new code, search for similar implementations and mirror their structure, naming, and conventions.
- Prefer minimal, focused diffs. Avoid drive-by refactors and formatting churn.
- Preserve public APIs. Avoid breaking changes unless explicitly requested and justified.
- Keep layers clean. Do not introduce forbidden dependencies between packages.
---
## Module / Package Architecture (Layering)
Use a layered module structure with explicit dependencies:
| Layer | Purpose | Allowed Dependencies |
|-------|---------|---------------------|
| `*.Domain.Shared` | Constants, enums, shared types safe for all layers and 3rd-party clients. MUST NOT contain entities, repositories, domain services, or business objects. | None |
| `*.Domain` | Entities/aggregate roots, repository interfaces, domain services. | Domain.Shared |
| `*.Application.Contracts` | Application service interfaces and DTOs. | Domain.Shared |
| `*.Application` | Application service implementations. | Domain, Application.Contracts |
| `*.EntityFrameworkCore` / `*.MongoDb` | ORM integration packages. MUST NOT depend on other layers. | Domain only |
| `*.HttpApi` | REST controllers. MUST depend ONLY on Application.Contracts (NOT Application). | Application.Contracts |
| `*.HttpApi.Client` | Remote client proxies. MUST depend ONLY on Application.Contracts. | Application.Contracts |
| `*.Web` | UI layer. MUST depend ONLY on HttpApi. | HttpApi |
### Dependency Direction
```
Web -> HttpApi -> Application.Contracts
Application -> Domain + Application.Contracts
Domain -> Domain.Shared
ORM integration -> Domain
```
Do not leak web concerns into application/domain.
---
## Domain Layer – Entities & Aggregate Roots
- Define entities in the domain layer.
- Entities must be valid at creation:
- Provide a primary constructor that enforces invariants.
- Always include a `protected` parameterless constructor for ORMs.
- Always initialize sub-collections in the primary constructor.
- Do NOT generate Guid keys inside constructors; accept `id` and generate using `IGuidGenerator` from the calling code.
- Make members `virtual` where appropriate (ORM/proxy compatibility).
- Protect consistency:
- Use non-public setters (`private`/`protected`/`internal`) when needed.
- Provide meaningful domain methods for state transitions.
### Aggregate Roots
- Always use a single `Id` property. Do NOT use composite keys.
- Prefer `Guid` keys for aggregate roots.
- Inherit from `AggregateRoot<TKey>` or audited base classes as required.
- Keep aggregates small. Avoid large sub-collections unless necessary.
### References
- Reference other aggregate roots by Id only.
- Do NOT add navigation properties to other aggregate roots.
---
## Repositories
- Define repository interfaces in the domain layer.
- Create one dedicated repository interface per aggregate root (e.g., `IProductRepository`).
- Public repository interfaces exposed by modules:
- SHOULD inherit from `IBasicRepository<TEntity, TKey>` (or `IReadOnlyRepository<...>` when suitable).
- SHOULD NOT expose `IQueryable` in the public contract.
- Internal implementations MAY use `IRepository<TEntity, TKey>` and `IQueryable` as needed.
- Do NOT define repositories for non-aggregate-root entities.
### Method Conventions
- All methods async.
- Include optional `CancellationToken cancellationToken = default` in every method.
- For single-entity returning methods: include `bool includeDetails = true`.
- For list returning methods: include `bool includeDetails = false`.
- Do NOT return composite projection classes like `UserWithRoles`. Use `includeDetails` for eager-loading.
- Avoid projection-only view models from repositories by default; only allow when performance is critical.
---
## Domain Services
- Define domain services in the domain layer.
- Default: do NOT create interfaces for domain services unless necessary (mocking/multiple implementations).
- Naming: use `*Manager` suffix.
### Method Guidelines
- Focus on operations that enforce domain invariants and business rules.
- Query methods are acceptable when they encapsulate domain-specific lookup logic (e.g., normalized lookups, caching, complex resolution). Simple queries belong in repositories.
- Define methods that mutate state and enforce domain rules.
- Use specific, intention-revealing names (avoid generic `UpdateXAsync`).
- Accept valid domain objects as parameters; do NOT accept/return DTOs.
- On rule violations, throw `BusinessException` (or custom business exceptions).
- Use unique, namespaced error codes suitable for localization (e.g., `IssueTracking:ConcurrentOpenIssueLimit`).
- Do NOT depend on authenticated user logic; pass required values from application layer.
---
## Application Services
### Contracts
- Define one interface per application service in `*.Application.Contracts`.
- Interfaces must inherit from `IApplicationService`.
- Naming: `I*AppService`.
- Do NOT accept/return entities. Use DTOs and primitive parameters.
### Method Naming & Shapes
- All service methods async and end with `Async`.
- Do not repeat entity names in method names (use `GetAsync`, not `GetProductAsync`).
**Standard CRUD:**
```csharp
Task<ProductDto> GetAsync(Guid id);
Task<PagedResultDto<ProductDto>> GetListAsync(GetProductListInput input);
Task<ProductDto> CreateAsync(CreateProductInput input);
Task<ProductDto> UpdateAsync(Guid id, UpdateProductInput input); // id NOT inside DTO
Task DeleteAsync(Guid id);
```
### DTO Usage (Inputs)
- Do not include unused properties.
- Do NOT share input DTOs between methods.
- Do NOT use inheritance between input DTOs (except rare abstract base DTO cases; be very cautious).
### Implementation
- Application layer must be independent of web.
- Implement interfaces in `*.Application`, name `ProductAppService` for `IProductAppService`.
- Inherit from `ApplicationService`.
- Make all public methods `virtual`.
- Avoid private helper methods; prefer `protected virtual` helpers for extensibility.
### Data Access
- Use dedicated repositories (e.g., `IProductRepository`).
- Do NOT put LINQ/SQL queries inside application service methods; repositories perform queries.
### Entity Mutation
- Load required entities from repositories.
- Mutate using domain methods.
- Call repository `UpdateAsync` after updates (do not assume change tracking).
### Files
- Do NOT use web types like `IFormFile` or `Stream` in application services.
- Controllers handle upload; pass `byte[]` (or similar) to application services.
### Cross-Service Calls
- Do NOT call other application services within the same module.
- For reuse, push logic into domain layer or extract shared helpers carefully.
- You MAY call other modules' application services only via their Application.Contracts.
---
## DTO Conventions
- Define DTOs in `*.Application.Contracts`.
- Prefer ABP base DTO types (`EntityDto<TKey>`, audited DTOs).
- For aggregate roots, prefer extensible DTO base types so extra properties can map.
- DTO properties: public getters/setters.
### Input DTO Validation
- Use data annotations.
- Reuse constants from Domain.Shared wherever possible.
### General Rules
- Avoid logic in DTOs; only implement `IValidatableObject` when necessary.
- Do NOT use `[Serializable]` attribute (BinaryFormatter is obsolete); ABP uses JSON serialization.
### Output DTO Strategy
- Prefer a Basic DTO and a Detailed DTO; avoid many variants.
- Detailed DTOs: include reference details as nested basic DTOs; avoid duplicating raw FK ids unnecessarily.
---
## EF Core Integration
- Define a separate DbContext interface + class per module.
- Do NOT rely on lazy loading; do NOT enable lazy loading.
### DbContext Interface
```csharp
[ConnectionStringName("ModuleName")]
public interface IModuleNameDbContext : IEfCoreDbContext
{
DbSet<Product> Products { get; } // No setters, aggregate roots only
}
```
### DbContext Class
```csharp
[ConnectionStringName("ModuleName")]
public class ModuleNameDbContext : AbpDbContext<ModuleNameDbContext>, IModuleNameDbContext
{
public static string TablePrefix { get; set; } = ModuleNameConsts.DefaultDbTablePrefix;
public static string? Schema { get; set; } = ModuleNameConsts.DefaultDbSchema;
public DbSet<Product> Products { get; set; }
}
```
### Table Prefix/Schema
- Provide static `TablePrefix` and `Schema` defaulted from constants.
- Use short prefixes; `Abp` prefix reserved for ABP core modules.
- Default schema should be `null`.
### Model Mapping
- Do NOT configure entities directly inside `OnModelCreating`.
- Create `ModelBuilder` extension method `ConfigureX()` and call it.
- Call `b.ConfigureByConvention()` for each entity.
### Repository Implementations
- Inherit from `EfCoreRepository<TDbContextInterface, TEntity, TKey>`.
- Use DbContext interface as generic parameter.
- Pass cancellation tokens using `GetCancellationToken(cancellationToken)`.
- Implement `IncludeDetails(include)` extension per aggregate root with sub-collections.
- Override `WithDetailsAsync()` where needed.
---
## MongoDB Integration
- Define a separate MongoDbContext interface + class per module.
### MongoDbContext Interface
```csharp
[ConnectionStringName("ModuleName")]
public interface IModuleNameMongoDbContext : IAbpMongoDbContext
{
IMongoCollection<Product> Products { get; } // Aggregate roots only
}
```
### MongoDbContext Class
```csharp
public class ModuleNameMongoDbContext : AbpMongoDbContext, IModuleNameMongoDbContext
{
public static string CollectionPrefix { get; set; } = ModuleNameConsts.DefaultDbTablePrefix;
}
```
### Mapping
- Do NOT configure directly inside `CreateModel`.
- Create `IMongoModelBuilder` extension method `ConfigureX()` and call it.
### Repository Implementations
- Inherit from `MongoDbRepository<TMongoDbContextInterface, TEntity, TKey>`.
- Pass cancellation tokens using `GetCancellationToken(cancellationToken)`.
- Ignore `includeDetails` for MongoDB in most cases (documents load sub-collections).
- Prefer `GetQueryableAsync()` to ensure ABP data filters are applied.
---
## ABP Module Classes
- Every package must have exactly one `AbpModule` class.
- Naming: `Abp[ModuleName][Layer]Module` (e.g., `AbpIdentityDomainModule`, `AbpIdentityApplicationModule`).
- Use `[DependsOn(typeof(...))]` to declare module dependencies explicitly.
- Override `ConfigureServices` for DI registration and configuration.
- Override `OnApplicationInitialization` sparingly; prefer `ConfigureServices` when possible.
- Each module must be usable standalone; avoid hidden cross-module coupling.
---
## Framework Extensibility
- All public and protected members should be `virtual` for inheritance-based extensibility.
- Prefer `protected virtual` over `private` for helper methods to allow overriding.
- Use `[Dependency(ReplaceServices = true)]` patterns for services intended to be replaceable.
- Provide extension points via interfaces and virtual methods.
- Document extension points with XML comments explaining intended usage.
- Consider providing `*Options` classes for configuration-based extensibility.
---
## Backward Compatibility
- Do NOT remove or rename public API members without a deprecation cycle.
- Use `[Obsolete("Message. Use X instead.")]` with clear migration guidance before removal.
- Maintain binary and source compatibility within major versions.
- Add new optional parameters with defaults; do not change existing method signatures.
- When adding new abstract members to base classes, provide default implementations if possible.
- Prefer adding new interfaces over modifying existing ones.
---
## Localization Resources
- Define localization resources in Domain.Shared.
- Resource class naming: `[ModuleName]Resource` (e.g., `IdentityResource`, `PermissionManagementResource`).
- JSON files under `/Localization/[ModuleName]/` directory.
- Use `LocalizableString.Create<TResource>("Key")` for localizable exceptions and messages.
- All user-facing strings must be localized; no hardcoded English text in code.
- Error codes should be namespaced: `ModuleName:ErrorCode` (e.g., `Identity:UserNameAlreadyExists`).
---
## Settings & Features
- Define settings in `*SettingDefinitionProvider` in Domain.Shared or Domain.
- Setting names must follow `Abp.[ModuleName].[SettingName]` convention.
- Define features in `*FeatureDefinitionProvider` in Domain.Shared.
- Feature names must follow `[ModuleName].[FeatureName]` convention.
- Use constants for setting/feature names; never hardcode strings.
---
## Permissions
- Define permissions in `*PermissionDefinitionProvider` in Application.Contracts.
- Permission names must follow `[ModuleName].[Permission]` convention.
- Use constants for permission names (e.g., `IdentityPermissions.Users.Create`).
- Group related permissions logically.
---
## Event Bus & Distributed Events
- Use `ILocalEventBus` for intra-module communication within the same process.
- Use `IDistributedEventBus` for cross-module or cross-service communication.
- Define Event Transfer Objects (ETOs) in Domain.Shared for distributed events.
- ETO naming: `[EntityName][Action]Eto` (e.g., `UserCreatedEto`, `OrderCompletedEto`).
- Event handlers belong in the Application layer.
- ETOs should be simple, serializable, and contain only primitive types or nested ETOs.
---
## Testing
- Unit tests: `*.Tests` projects for isolated logic testing with mocked dependencies.
- Integration tests: `*.EntityFrameworkCore.Tests` / `*.MongoDB.Tests` for repository and DB tests.
- Use `AbpIntegratedTest<TModule>` or `AbpApplicationTestBase<TModule>` base classes.
- Test modules should use `[DependsOn]` on the module under test.
- Use `Shouldly` assertions (ABP convention).
- Test both EF Core and MongoDB implementations when the module supports both.
- Include tests for permission checks, validation, and edge cases.
- Name test methods: `MethodName_Scenario_ExpectedResult` or `Should_ExpectedBehavior_When_Condition`.
---
## Contribution Discipline (PR / Issues / Tests)
- Before significant changes, align via GitHub issue/discussion.
### PRs
- Keep changes scoped and reviewable.
- Add/update unit/integration tests relevant to the change.
- Build and run tests for the impacted area when possible.
### Localization
- Prefer the `abp translate` workflow for adding missing translations (generate `abp-translation.json`, fill, apply, then PR).
---
## Review Checklist
- [ ] Layer dependencies respected (no forbidden references).
- [ ] No `IQueryable` leaking into public repository contracts.
- [ ] Entities maintain invariants; Guid id generation not inside constructors.
- [ ] Repositories follow async + CancellationToken + includeDetails conventions.
- [ ] No web types in application services.
- [ ] DTOs in contracts, validated, minimal, no logic.
- [ ] EF/Mongo integration follows context + mapping + repository patterns.
- [ ] Public members are `virtual` for extensibility.
- [ ] Backward compatibility maintained; no breaking changes without deprecation.
- [ ] Minimal diff; no unnecessary API surface expansion.
Loading…
Cancel
Save