# Integration Standard: gRPC

## When to Use gRPC
✅ Internal service-to-service communication (high performance)
✅ Streaming (server-streaming for real-time data, client-streaming for uploads)
✅ Multi-language microservices (proto contracts shared across .NET, Go, Python)
❌ Browser-to-server (use gRPC-Web or REST instead)
❌ Debugging priority (binary format is harder to inspect than JSON)

## Setup
```xml
<PackageReference Include="Grpc.AspNetCore" Version="2.*" />
```

```csharp
// Program.cs
builder.Services.AddGrpc(opts =>
{
    opts.EnableDetailedErrors = builder.Environment.IsDevelopment();
    opts.MaxReceiveMessageSize = 4 * 1024 * 1024; // 4 MB
});
app.MapGrpcService<OrdersService>();
```

## Proto Definition
```protobuf
syntax = "proto3";
option csharp_namespace = "MyApp.Grpc";

service Orders {
  rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
  rpc GetOrder(GetOrderRequest) returns (OrderResponse);
  rpc StreamOrders(StreamOrdersRequest) returns (stream OrderResponse);
}

message CreateOrderRequest {
  string user_id = 1;
  repeated OrderItem items = 2;
}

message CreateOrderResponse {
  string order_id = 1;
  double total = 2;
}
```

## Service Implementation
```csharp
public class OrdersService : Orders.OrdersBase
{
    public override async Task<CreateOrderResponse> CreateOrder(
        CreateOrderRequest request, ServerCallContext context)
    {
        var cmd = new CreateOrderCommand(
            Guid.Parse(request.UserId),
            request.Items.Select(i => new OrderItem(i.ProductId, i.Quantity)).ToList());

        var result = await _sender.Send(cmd, context.CancellationToken);

        return new CreateOrderResponse
        {
            OrderId = result.OrderId.ToString(),
            Total = (double)result.Total
        };
    }

    public override async Task StreamOrders(
        StreamOrdersRequest request,
        IServerStreamWriter<OrderResponse> responseStream,
        ServerCallContext context)
    {
        await foreach (var order in _service.GetOrderStreamAsync(context.CancellationToken))
        {
            await responseStream.WriteAsync(MapToResponse(order), context.CancellationToken);
            if (context.CancellationToken.IsCancellationRequested) break;
        }
    }
}
```

## Client Setup
```csharp
// Program.cs (client service)
builder.Services.AddGrpcClient<Orders.OrdersClient>(opts =>
{
    opts.Address = new Uri(config["Services:OrdersUrl"]);
})
.ConfigureChannel(opts =>
{
    opts.HttpHandler = new SocketsHttpHandler
    {
        PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan,
        KeepAlivePingDelay = TimeSpan.FromSeconds(60),
        KeepAlivePingTimeout = TimeSpan.FromSeconds(30)
    };
});
```

## Error Handling
```csharp
// Map exceptions to gRPC status codes
try
{
    return await _service.ProcessAsync(request.Id);
}
catch (NotFoundException)
{
    throw new RpcException(new Status(StatusCode.NotFound, "Order not found"));
}
catch (ValidationException ex)
{
    throw new RpcException(new Status(StatusCode.InvalidArgument, ex.Message));
}
```
