# StellaOps Router Example This example demonstrates the StellaOps Router, Gateway, and Microservice SDK working together. ## Overview The example includes: - **Examples.Gateway** - HTTP gateway that routes requests to microservices - **Examples.Billing.Microservice** - Sample billing service with typed and streaming endpoints - **Examples.Inventory.Microservice** - Sample inventory service demonstrating multi-service routing - **Examples.Integration.Tests** - End-to-end integration tests ## Prerequisites - .NET 10 SDK - Docker and Docker Compose (for containerized deployment) ## Project Structure ``` examples/router/ ├── Examples.Router.sln ├── docker-compose.yaml ├── README.md ├── src/ │ ├── Examples.Gateway/ │ │ ├── Program.cs │ │ ├── router.yaml │ │ └── appsettings.json │ ├── Examples.Billing.Microservice/ │ │ ├── Program.cs │ │ ├── microservice.yaml │ │ └── Endpoints/ │ │ ├── CreateInvoiceEndpoint.cs │ │ ├── GetInvoiceEndpoint.cs │ │ └── UploadAttachmentEndpoint.cs │ └── Examples.Inventory.Microservice/ │ ├── Program.cs │ └── Endpoints/ │ ├── ListItemsEndpoint.cs │ └── GetItemEndpoint.cs └── tests/ └── Examples.Integration.Tests/ ``` ## Running Locally ### Build the Solution ```bash cd examples/router dotnet build Examples.Router.sln ``` ### Run with Docker Compose ```bash docker-compose up --build ``` This starts: - Gateway on port 8080 (HTTP) and 5100 (TCP transport) - Billing microservice - Inventory microservice - RabbitMQ (optional, for message-based transport) ### Run Without Docker Start each service in separate terminals: ```bash # Terminal 1: Gateway cd src/Examples.Gateway dotnet run # Terminal 2: Billing Microservice cd src/Examples.Billing.Microservice dotnet run # Terminal 3: Inventory Microservice cd src/Examples.Inventory.Microservice dotnet run ``` ## Example API Calls ### Billing Service Create an invoice: ```bash curl -X POST http://localhost:8080/invoices \ -H "Content-Type: application/json" \ -d '{"customerId": "CUST-001", "amount": 99.99, "description": "Service fee"}' ``` Get an invoice: ```bash curl http://localhost:8080/invoices/INV-12345 ``` Upload an attachment (streaming): ```bash curl -X POST http://localhost:8080/invoices/INV-12345/attachments \ -H "Content-Type: application/octet-stream" \ --data-binary @document.pdf ``` ### Inventory Service List items: ```bash curl "http://localhost:8080/items?page=1&pageSize=20" ``` List items by category: ```bash curl "http://localhost:8080/items?category=widgets" ``` Get a specific item: ```bash curl http://localhost:8080/items/SKU-001 ``` ## Adding New Endpoints ### 1. Create the Endpoint Class ```csharp using StellaOps.Microservice; [StellaEndpoint("POST", "/orders", TimeoutSeconds = 30)] public sealed class CreateOrderEndpoint : IStellaEndpoint { public Task HandleAsync( CreateOrderRequest request, CancellationToken cancellationToken) { // Implementation return Task.FromResult(new CreateOrderResponse { OrderId = "ORD-123" }); } } ``` ### 2. Register in Program.cs ```csharp builder.Services.AddScoped(); ``` ### 3. Update router.yaml (if needed) Add routing rules for the new endpoint path. ## Streaming Endpoints For endpoints that handle large payloads (file uploads, etc.), implement `IRawStellaEndpoint`: ```csharp [StellaEndpoint("POST", "/files/{id}", SupportsStreaming = true)] public sealed class UploadFileEndpoint : IRawStellaEndpoint { public async Task HandleAsync( RawRequestContext context, CancellationToken cancellationToken) { var id = context.PathParameters["id"]; // Stream body directly without buffering await using var stream = context.Body; // Process stream... return RawResponse.Ok("{}"); } } ``` ## Cancellation Behavior All endpoints receive a `CancellationToken` that is triggered when: 1. The client disconnects 2. The request timeout is exceeded 3. The gateway shuts down Always respect the cancellation token in long-running operations: ```csharp public async Task HandleAsync(Request request, CancellationToken ct) { // Check cancellation periodically ct.ThrowIfCancellationRequested(); // Or pass to async operations await SomeLongOperation(ct); } ``` ## Payload Limits Default limits are configured in `router.yaml`: ```yaml payloadLimits: maxRequestBodySizeBytes: 10485760 # 10 MB maxChunkSizeBytes: 65536 # 64 KB ``` For streaming endpoints, the body is not buffered so these limits apply per-chunk. ## Running Tests ```bash cd tests/Examples.Integration.Tests dotnet test ``` The integration tests verify: - End-to-end request routing - Multi-service registration - Streaming uploads - Request cancellation - Payload limit enforcement ## Configuration ### Gateway (router.yaml) ```yaml # Microservice routing rules services: billing: routes: - path: /invoices methods: [GET, POST] - path: /invoices/{id} methods: [GET, PUT, DELETE] - path: /invoices/{id}/attachments methods: [POST] inventory: routes: - path: /items methods: [GET] - path: /items/{sku} methods: [GET] ``` ### Microservice (microservice.yaml) ```yaml service: name: billing version: 1.0.0 region: demo endpoints: - path: /invoices method: POST timeoutSeconds: 30 - path: /invoices/{id} method: GET timeoutSeconds: 10 routers: - host: localhost port: 5100 transportType: InMemory ``` ## Troubleshooting ### Microservice not registering Check that: 1. Gateway is running and healthy 2. Router host/port in microservice.yaml matches gateway 3. Network connectivity between services ### Request timeouts Increase the timeout in the endpoint attribute: ```csharp [StellaEndpoint("POST", "/long-operation", TimeoutSeconds = 120)] ``` ### Streaming not working Ensure the endpoint: 1. Is marked with `SupportsStreaming = true` 2. Implements `IRawStellaEndpoint` 3. Does not buffer the entire body before processing ## License AGPL-3.0-or-later