dotnet-cqrs/docs/grpc-integration/README.md

503 lines
11 KiB
Markdown

# gRPC Integration Overview
Expose commands and queries via high-performance gRPC services with automatic code generation.
## What is gRPC Integration?
The `Svrnty.CQRS.Grpc` package with `Svrnty.CQRS.Grpc.Generators` source generator provides automatic gRPC service implementations for all registered commands and queries.
**Key Features:**
-**Automatic service generation** - Source generators create implementations
-**Google Rich Error Model** - Structured validation errors
-**High performance** - Binary Protocol Buffers
-**Strong typing** - Compile-time safety
-**gRPC reflection** - Tool support (grpcurl, Postman)
-**Bidirectional streaming** - Real-time communication
-**Cross-platform** - Works with any gRPC client
## Quick Start
### Installation
```bash
dotnet add package Svrnty.CQRS.Grpc
dotnet add package Svrnty.CQRS.Grpc.Generators
dotnet add package Grpc.AspNetCore
```
### Basic Setup
**1. Define .proto file:**
```protobuf
syntax = "proto3";
package myapp;
import "google/protobuf/empty.proto";
service CommandService {
rpc CreateUser (CreateUserCommand) returns (CreateUserResponse);
rpc DeleteUser (DeleteUserCommand) returns (google.protobuf.Empty);
}
service QueryService {
rpc GetUser (GetUserQuery) returns (UserDto);
}
message CreateUserCommand {
string name = 1;
string email = 2;
}
message CreateUserResponse {
int32 user_id = 1;
}
message DeleteUserCommand {
int32 user_id = 1;
}
message GetUserQuery {
int32 user_id = 1;
}
message UserDto {
int32 id = 1;
string name = 2;
string email = 3;
}
```
**2. Configure services:**
```csharp
var builder = WebApplication.CreateBuilder(args);
// Register CQRS services
builder.Services.AddSvrntyCQRS();
builder.Services.AddDefaultCommandDiscovery();
builder.Services.AddDefaultQueryDiscovery();
// Register commands and queries
builder.Services.AddCommand<CreateUserCommand, int, CreateUserCommandHandler>();
builder.Services.AddCommand<DeleteUserCommand, DeleteUserCommandHandler>();
builder.Services.AddQuery<GetUserQuery, UserDto, GetUserQueryHandler>();
// Add gRPC
builder.Services.AddGrpc();
var app = builder.Build();
// Map auto-generated service implementations
app.MapGrpcService<CommandServiceImpl>();
app.MapGrpcService<QueryServiceImpl>();
// Enable reflection for tools
app.MapGrpcReflectionService();
app.Run();
```
**3. Source generator automatically creates:**
- `CommandServiceImpl` class implementing `CommandService.CommandServiceBase`
- `QueryServiceImpl` class implementing `QueryService.QueryServiceBase`
## How It Works
```
┌─────────────────────────────┐
│ Build Time │
├─────────────────────────────┤
│ 1. Read .proto files │
│ 2. Discover commands/queries│
│ 3. Generate service impls │
│ 4. Compile into assembly │
└─────────────────────────────┘
┌─────────────────────────────┐
│ Runtime │
├─────────────────────────────┤
│ gRPC Request │
│ → Deserialize protobuf │
│ → Validate │
│ → Authorize │
│ → Execute handler │
│ → Serialize response │
└─────────────────────────────┘
```
## Commands via gRPC
### Command Without Result
```csharp
public record DeleteUserCommand
{
public int UserId { get; init; }
}
public class DeleteUserCommandHandler : ICommandHandler<DeleteUserCommand>
{
public async Task HandleAsync(DeleteUserCommand command, CancellationToken cancellationToken)
{
// Delete user logic
}
}
```
**.proto definition:**
```protobuf
service CommandService {
rpc DeleteUser (DeleteUserCommand) returns (google.protobuf.Empty);
}
message DeleteUserCommand {
int32 user_id = 1;
}
```
**gRPC Client:**
```csharp
var client = new CommandService.CommandServiceClient(channel);
var request = new DeleteUserCommand { UserId = 123 };
await client.DeleteUserAsync(request);
```
### Command With Result
```csharp
public record CreateUserCommand
{
public string Name { get; init; } = string.Empty;
public string Email { get; init; } = string.Empty;
}
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand, int>
{
public async Task<int> HandleAsync(CreateUserCommand command, CancellationToken cancellationToken)
{
// Create user and return ID
return newUserId;
}
}
```
**.proto definition:**
```protobuf
service CommandService {
rpc CreateUser (CreateUserCommand) returns (CreateUserResponse);
}
message CreateUserCommand {
string name = 1;
string email = 2;
}
message CreateUserResponse {
int32 user_id = 1;
}
```
**gRPC Client:**
```csharp
var client = new CommandService.CommandServiceClient(channel);
var request = new CreateUserCommand
{
Name = "John Doe",
Email = "john@example.com"
};
var response = await client.CreateUserAsync(request);
var userId = response.UserId;
```
## Queries via gRPC
```csharp
public record GetUserQuery
{
public int UserId { get; init; }
}
public record UserDto
{
public int Id { get; init; }
public string Name { get; init; } = string.Empty;
public string Email { get; init; } = string.Empty;
}
public class GetUserQueryHandler : IQueryHandler<GetUserQuery, UserDto>
{
public async Task<UserDto> HandleAsync(GetUserQuery query, CancellationToken cancellationToken)
{
// Fetch and return user
}
}
```
**.proto definition:**
```protobuf
service QueryService {
rpc GetUser (GetUserQuery) returns (UserDto);
}
message GetUserQuery {
int32 user_id = 1;
}
message UserDto {
int32 id = 1;
string name = 2;
string email = 3;
}
```
**gRPC Client:**
```csharp
var client = new QueryService.QueryServiceClient(channel);
var request = new GetUserQuery { UserId = 123 };
var user = await client.GetUserAsync(request);
```
## Validation
### Automatic Validation with Rich Error Model
```csharp
public class CreateUserCommandValidator : AbstractValidator<CreateUserCommand>
{
public CreateUserCommandValidator()
{
RuleFor(x => x.Name)
.NotEmpty()
.WithMessage("Name is required");
RuleFor(x => x.Email)
.EmailAddress()
.WithMessage("Valid email address is required");
}
}
```
**Validation failure response:**
```protobuf
google.rpc.Status {
code: 3 // INVALID_ARGUMENT
message: "Validation failed"
details: [
google.rpc.BadRequest {
field_violations: [
{ field: "name", description: "Name is required" },
{ field: "email", description: "Valid email address is required" }
]
}
]
}
```
**Client handling:**
```csharp
using Grpc.Core;
using Google.Rpc;
try
{
var response = await client.CreateUserAsync(request);
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.InvalidArgument)
{
var status = ex.GetRpcStatus();
var badRequest = status.GetDetail<BadRequest>();
foreach (var violation in badRequest.FieldViolations)
{
Console.WriteLine($"{violation.Field}: {violation.Description}");
}
}
```
## Performance Benefits
### Binary Protocol
gRPC uses Protocol Buffers (binary format) instead of JSON:
**JSON (HTTP):**
```json
{
"id": 123,
"name": "John Doe",
"email": "john@example.com"
}
```
**Size:** ~71 bytes
**Protobuf (gRPC):**
```
Binary representation
```
**Size:** ~35 bytes
**Result:** ~50% smaller payload
### HTTP/2 Multiplexing
- Multiple requests over single connection
- Header compression
- Server push capability
- Bidirectional streaming
## gRPC vs HTTP Comparison
| Feature | gRPC | HTTP (Minimal API) |
|---------|------|-------------------|
| Protocol | HTTP/2 | HTTP/1.1 or HTTP/2 |
| Format | Protobuf (binary) | JSON (text) |
| Performance | Very fast | Fast |
| Payload Size | Small | Larger |
| Browser Support | Limited (grpc-web) | Full |
| Tooling | grpcurl, Postman | curl, Postman, Swagger |
| Streaming | Native bidirectional | Server-Sent Events |
| Code Generation | Automatic | Automatic |
| Type Safety | Strong | Strong |
### When to Use gRPC
**Use gRPC for:**
- Microservices communication
- High-performance APIs
- Real-time bidirectional streaming
- Internal APIs
- Polyglot environments
### When to Use HTTP
**Use HTTP for:**
- Public APIs
- Browser-based clients
- Simple REST APIs
- Legacy system integration
- Human-readable debugging
### Dual Protocol
**Best of both worlds:**
```csharp
// Same handlers, multiple protocols
builder.Services.AddCommand<CreateUserCommand, int, CreateUserCommandHandler>();
// HTTP endpoints
app.MapSvrntyCommands();
// gRPC endpoints
app.MapGrpcService<CommandServiceImpl>();
```
Clients choose their preferred protocol!
## Documentation
### [Getting Started](getting-started-grpc.md)
First gRPC service:
- Installation
- .proto file creation
- Service registration
- Testing with grpcurl
### [Proto File Setup](proto-file-setup.md)
.proto file creation:
- Syntax and conventions
- Message definitions
- Service definitions
- Importing common types
### [Source Generators](source-generators.md)
How code generation works:
- Build-time generation
- Generated code structure
- Customization options
- Troubleshooting
### [Service Implementation](service-implementation.md)
Generated service implementations:
- CommandServiceImpl
- QueryServiceImpl
- Validation integration
- Authorization integration
### [gRPC Reflection](grpc-reflection.md)
gRPC reflection for tools:
- Enabling reflection
- Using grpcurl
- Postman support
- Service discovery
### [gRPC Clients](grpc-clients.md)
Consuming gRPC services:
- C# client
- TypeScript client
- Go client
- Python client
### [gRPC Troubleshooting](grpc-troubleshooting.md)
Common issues:
- Connection errors
- Validation errors
- Code generation issues
- Performance tuning
## Best Practices
### ✅ DO
- Use gRPC for microservices
- Define clear .proto contracts
- Use gRPC reflection in development
- Handle RpcException properly
- Version your services
- Use deadlines/timeouts
- Enable compression
### ❌ DON'T
- Don't skip error handling
- Don't expose gRPC publicly without security
- Don't ignore validation
- Don't use gRPC for browser apps without grpc-web
- Don't forget cancellation tokens
## What's Next?
- **[Getting Started](getting-started-grpc.md)** - Create your first gRPC service
- **[Proto File Setup](proto-file-setup.md)** - Learn .proto file conventions
- **[Source Generators](source-generators.md)** - Understand code generation
- **[Service Implementation](service-implementation.md)** - Explore generated code
- **[gRPC Clients](grpc-clients.md)** - Build gRPC clients
## See Also
- [Commands Overview](../core-features/commands/README.md)
- [Queries Overview](../core-features/queries/README.md)
- [Validation Overview](../core-features/validation/README.md)
- [HTTP Integration](../http-integration/README.md)
- [Getting Started: Choosing HTTP or gRPC](../getting-started/06-choosing-http-or-grpc.md)