dotnet-cqrs/.claude/rules/grpc.md
Mathias Beaulieu-Duncan a4525bad6a Add Claude Code harness: rules, skills, hooks, and editorconfig
- Add path-specific rules for commands/queries, dynamic queries, validation, and gRPC
- Add /add-command, /add-query, /add-dynamic-query scaffolding skills
- Add project settings with post-edit formatting, proto validation, and build-gate hooks
- Add .editorconfig codifying existing code style conventions
- Trim CLAUDE.md from 414 to 130 lines (domain details moved to rules)
- Add .harness-version tracking for the shared claude-harness repo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 03:30:27 -04:00

3.2 KiB

paths
**/*.proto
**/Protos/**
*Grpc*

gRPC Integration

Proto File Conventions

Proto files live in Protos/ directory. The source generator (Svrnty.CQRS.Grpc.Generators) auto-generates service implementations at compile time.

Service Structure

service CommandService {
  rpc PlaceOrder (PlaceOrderCommandRequest) returns (PlaceOrderCommandResponse);
}

service QueryService {
  rpc FetchUserById (FetchUserByIdQueryRequest) returns (FetchUserByIdQueryResponse);
}

service DynamicQueryService {
  rpc QueryOrders (DynamicQueryOrdersRequest) returns (DynamicQueryOrdersResponse);
}

Naming Rules

  • RPC method name: command/query name without the Command/Query suffix
  • Request message: {Name}CommandRequest or {Name}QueryRequest
  • Response message: {Name}CommandResponse or {Name}QueryResponse
  • Dynamic query request: DynamicQuery{PluralEntity}Request
  • Dynamic query response: DynamicQuery{PluralEntity}Response
  • Fields use snake_case (proto convention), mapped case-insensitively to C# PascalCase properties

Field Mapping

C# property names must match proto field names (case-insensitive):

C# Property Proto Field
UserId user_id
Name name
EmailAddress email_address
OrderItems order_items

Type Mapping

C# Proto
int int32
long int64
string string
bool bool
double double
float float
List<T> repeated T
complex type message

Dynamic Query Messages (standard, reuse across entities)

message DynamicQueryFilter {
  string path = 1;
  int32 type = 2;       // PoweredSoft.DynamicQuery.Core.FilterType
  string value = 3;
  repeated DynamicQueryFilter and = 4;
  repeated DynamicQueryFilter or = 5;
}

message DynamicQuerySort {
  string path = 1;
  bool ascending = 2;
}

message DynamicQueryGroup {
  string path = 1;
}

message DynamicQueryAggregate {
  string path = 1;
  int32 type = 2;       // PoweredSoft.DynamicQuery.Core.AggregateType
}

Source Generator Behavior

The generator in Svrnty.CQRS.Grpc.Generators auto-creates:

  • CommandServiceImpl — implements CommandService.CommandServiceBase
  • QueryServiceImpl — implements QueryService.QueryServiceBase
  • DynamicQueryServiceImpl — implements DynamicQueryService.DynamicQueryServiceBase
  • GrpcServiceRegistration — auto-registration code

Generated implementations handle:

  1. Request-to-POCO property mapping
  2. Validator invocation (if registered) with Google Rich Error Model errors
  3. Handler invocation with proper CancellationToken
  4. DI scope management via IServiceScopeFactory

Validation in gRPC

Validation errors return google.rpc.Status with BadRequest detail containing FieldViolations. This is automatic — do not manually validate in handlers.

Registration

builder.Services.AddSvrntyCqrs(cqrs =>
{
    cqrs.AddGrpc(grpc => grpc.EnableReflection()); // reflection for grpcurl/grpcui
});