Automated summary
Guides .NET 9 backend development in the EasyPlatform monorepo using CQRS commands and queries, entities, validation, migrations, background jobs, and the message bus.
Strengths
- Decision tree that routes each backend task to the correct pattern (command, query, event handler, migration, job, or message bus).
- Enforces strong architecture rules: side effects belong in Entity Event Handlers, cross-service communication goes through the message bus, and DTOs own their mapping.
- Specifies file organization and naming conventions for Application and Domain layers.
- Points to detailed reference files for deeper CQRS patterns.
Limitations
- Tightly coupled to the EasyPlatform monorepo and framework, so it is not reusable in other codebases.
- Depends on external reference documents (e.g., cqrs-patterns.md) that are not included in the SKILL.md.
- Assumes familiarity with in-house abstractions such as PlatformValidationResult and IPlatformQueryableRootRepository.
Use when implementing or modifying a .NET backend feature inside the EasyPlatform monorepo.
Do not use for a generic .NET project or a different framework outside the EasyPlatform context.
Security analysis
CautionThe skill is a conventional .NET/CQRS development guide. It contains no obfuscated payloads, no curl|sh, no credential or token exfiltration, no destructive filesystem commands, and no instructions to disable safety checks. The only elevated risk is inherent to its declared toolset: Bash plus Write/Edit allows shell execution and broad source modification, which is appropriate for backend scaffolding but warrants the 'caution' rating rather than 'safe'.
- •Declared allowed-tools include Bash, Write, and Edit, granting broad filesystem and shell capability within the repository
- •Skill instructs agents to scaffold/modify CQRS handlers, entities, and migrations without any explicit guardrails on file scope or migration execution
Examples
Create a SaveEmployeeCommand in the EasyPlatform backend that handles both create and update, with validation requiring a non-empty Name and returning an EmployeeDto.Add a GetEmployeeListQuery that supports paging, filters by a list of statuses and an optional search text, and returns EmployeeDto results.Add an entity event handler that sends a notification when an employee is updated, following the EasyPlatform side-effect pattern instead of putting it in the command handler.name: easyplatform-backend description: Complete Easy.Platform backend development for EasyPlatform. Covers CQRS commands/queries, entities, validation, migrations, background jobs, and message bus. Use for any .NET backend task in this monorepo. infer: true allowed-tools: Read, Write, Edit, Grep, Glob, Bash
Easy.Platform Backend Development
Complete backend development patterns for EasyPlatform .NET 9 microservices.
Quick Decision Tree
[Backend Task]
├── API endpoint?
│ ├── Creates/Updates/Deletes data → CQRS Command (§1)
│ └── Reads data → CQRS Query (§2)
├── Business entity? → Entity Development (§3)
├── Side effects (notifications, emails)? → Entity Event Handler (§4) - NEVER in command handlers!
├── Data transformation/backfill? → Migration (§5)
├── Scheduled/recurring task? → Background Job (§6)
└── Cross-service sync? → Message Bus (§7) - NEVER direct DB access!
File Organization
{Service}.Application/
├── UseCaseCommands/{Feature}/Save{Entity}Command.cs # Command+Handler+Result
├── UseCaseQueries/{Feature}/Get{Entity}ListQuery.cs # Query+Handler+Result
├── UseCaseEvents/{Feature}/*EntityEventHandler.cs # Side effects
├── BackgroundJobs/{Feature}/*Job.cs # Scheduled tasks
├── MessageBusProducers/*Producer.cs # Outbound events
├── MessageBusConsumers/{Entity}/*Consumer.cs # Inbound events
└── DataMigrations/*DataMigration.cs # Data migrations
{Service}.Domain/
└── Entities/{Entity}.cs # Domain entities
Critical Rules
- Repository:
IPlatformQueryableRootRepository<T>-- NEVER throw exceptions for validation - Validation:
PlatformValidationResultfluent API - Side Effects: Entity Event Handlers -- NEVER in command handlers
- DTO Mapping: DTOs own mapping via
PlatformEntityDto<T,K>.MapToEntity() - Cross-Service: Message bus -- NEVER direct database access
§1. CQRS Commands
File: UseCaseCommands/{Feature}/Save{Entity}Command.cs (Command + Result + Handler in ONE file)
public sealed class SaveEmployeeCommand : PlatformCqrsCommand<SaveEmployeeCommandResult>
{
public string? Id { get; set; }
public string Name { get; set; } = "";
public override PlatformValidationResult<IPlatformCqrsRequest> Validate()
=> base.Validate().And(_ => Name.IsNotNullOrEmpty(), "Name required");
}
public sealed class SaveEmployeeCommandResult : PlatformCqrsCommandResult
{
public EmployeeDto Entity { get; set; } = null!;
}
internal sealed class SaveEmployeeCommandHandler :
PlatformCqrsCommandApplicationHandler<SaveEmployeeCommand, SaveEmployeeCommandResult>
{
protected override async Task<SaveEmployeeCommandResult> HandleAsync(
SaveEmployeeCommand req, CancellationToken ct)
{
var entity = req.Id.IsNullOrEmpty()
? req.MapToNewEntity().With(e => e.CreatedBy = RequestContext.UserId())
: await repository.GetByIdAsync(req.Id, ct)
.EnsureFound().Then(e => req.UpdateEntity(e));
await entity.ValidateAsync(repository, ct).EnsureValidAsync();
await repository.CreateOrUpdateAsync(entity, ct);
return new SaveEmployeeCommandResult { Entity = new EmployeeDto(entity) };
}
}
⚠️ MUST READ: references/cqrs-patterns.md — full CQRS command patterns
§2. CQRS Queries
File: UseCaseQueries/{Feature}/Get{Entity}ListQuery.cs
public sealed class GetEmployeeListQuery : PlatformCqrsPagedQuery<GetEmployeeListQueryResult, EmployeeDto>
{
public List<Status> Statuses { get; set; } = [];
public string? SearchText { get; set; }
}
internal sealed class GetEmployeeListQueryHandler :
PlatformCqrsQueryApplicationHandler<GetEmployeeListQuery, GetEmployeeListQueryResult>
{
protected override async Task<GetEmployeeListQueryResult> HandleAsync(
GetEmployeeListQuery req, CancellationToken ct)
{
var qb = repository.GetQueryBuilder((uow, q) => q
.Where(e => e.CompanyId == RequestContext.CurrentCompanyId())
.WhereIf(req.Statuses.Any(), e => req.Statuses.Contains(e.Status))
.PipeIf(req.SearchText.IsNotNullOrEmpty(), q =>
searchService.Search(q, req.SearchText, Employee.DefaultFullTextSearchColumns())));
var (total, items) = await (
repository.CountAsync((uow, q) => qb(uow, q), ct),
repository.GetAllAsync((uow, q) => qb(uow, q)
.OrderByDescending(e => e.CreatedDate).PageBy(req.SkipCount, req.MaxResultCount), ct)
);
return new GetEmployeeListQueryResult(items.SelectList(e => new EmployeeDto(e)), total, req);
}
}
⚠️ MUST READ: references/cqrs-patterns.md — full CQRS query patterns
§3. Entity Development
File: {Service}.Domain/Entities/{Entity}.cs
[TrackFieldUpdatedDomainEvent]
public sealed class Employee : RootAuditedEntity<Employee, string, string>
{
[TrackFieldUpdatedDomainEvent] public string Name { get; set; } = "";
public string CompanyId { get; set; } = "";
[ComputedEntityProperty]
public string DisplayName { get => $"{Code} - {Name}"; set { } }
public static Expression<Func<Employee, bool>> OfCompanyExpr(string companyId) => e => e.CompanyId == companyId;
public static Expression<Func<Employee, bool>> UniqueExpr(string companyId, string code) => e => e.CompanyId == companyId && e.Code == code;
public static Expression<Func<Employee, object?>>[] DefaultFullTextSearchColumns() => [e => e.Name, e => e.Code];
public async Task<PlatformValidationResult> ValidateAsync(IRepository<Employee> repo, CancellationToken ct)
=> await PlatformValidationResult.Valid()
.And(() => Name.IsNotNullOrEmpty(), "Name required")
.AndNotAsync(() => repo.AnyAsync(e => e.Id != Id && e.Code == Code, ct), "Code exists");
}
⚠️ MUST READ: references/entity-patterns.md — full entity patterns
§4. Entity Event Handlers (Side Effects)
CRITICAL: NEVER call side effects in command handlers!
File: UseCaseEvents/{Feature}/Send{Action}On{Event}{Entity}EntityEventHandler.cs
internal sealed class SendNotificationOnCreateEmployeeEntityEventHandler
: PlatformCqrsEntityEventApplicationHandler<Employee>
{
public override async Task<bool> HandleWhen(PlatformCqrsEntityEvent<Employee> @event)
=> !@event.RequestContext.IsSeedingTestingData()
&& @event.CrudAction == PlatformCqrsEntityEventCrudAction.Created;
protected override async Task HandleAsync(PlatformCqrsEntityEvent<Employee> @event, CancellationToken ct)
=> await notificationService.SendAsync(@event.EntityData.Id);
}
⚠️ MUST READ: references/side-effects-patterns.md — side effects patterns
§5-7. Migrations, Jobs, Message Bus
⚠️ MUST READ: migration-patterns.md | job-patterns.md | messaging-patterns.md
Anti-Patterns
| Don't | Do |
| --------------------------------- | ----------------------------------------------- |
| throw new ValidationException() | PlatformValidationResult fluent API |
| Side effects in command handler | Entity Event Handler in UseCaseEvents/ |
| Direct cross-service DB access | Message bus |
| DTO mapping in handler | PlatformEntityDto.MapToEntity() |
| Separate Command/Handler files | ONE file: Command + Result + Handler |
| protected bool HandleWhen() | public override async Task<bool> HandleWhen() |
Checklist
- [ ] Service-specific repository, fluent validation (
.And(),.AndAsync()) - [ ] No side effects in command handlers, DTO mapping in DTO class
- [ ] Cross-service uses message bus, jobs have
maxConcurrent, migrations usedismissSendEvent: true
IMPORTANT Task Planning Notes
- Always plan and break many small todo tasks
- Always add a final review todo task to review the works done at the end to find any fix or enhancement needed
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/easyplatform-backend-development-net-mmlj2o1j/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/easyplatform-backend-development-net-mmlj2o1j' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/easyplatform-backend-development-net-mmlj2o1j/download' --output '.skill-downloads/easyplatform-backend-development-net-mmlj2o1j/SKILL.md.part' && mv '.skill-downloads/easyplatform-backend-development-net-mmlj2o1j/SKILL.md.part' '.skill-downloads/easyplatform-backend-development-net-mmlj2o1j/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
Tools declared by the author (check availability in your agent): Read, Write, Edit, Grep, Glob, Bash
No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.
Claude Code
test -f '.skill-downloads/easyplatform-backend-development-net-mmlj2o1j/SKILL.md' && test -s '.skill-downloads/easyplatform-backend-development-net-mmlj2o1j/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/easyplatform-backend' && cp -R '.skill-downloads/easyplatform-backend-development-net-mmlj2o1j' '.claude/skills/easyplatform-backend'OpenSkills
OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.
Official OpenSkills installationopenskills install './.skill-downloads/easyplatform-backend-development-net-mmlj2o1j'openskills read 'easyplatform-backend-development-net-mmlj2o1j'Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.
For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.
openskills syncManual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/easyplatform-backend-development-net-mmlj2o1j/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
React Hook Form Best Practices
Development
Master React Hook Form: avoid anti-patterns, manage subscriptions, and handle reset, dirty state, and controlled inputs correctly.
Emotion to Mantine Migration
Development
Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports.
Scope Creep Detector
Development
Analyzes git diffs against a stated intent to detect scope creep, unrelated files, broad PRs, and oversized changes, then suggests keep, split, or justify actions.