Multi Pipelines, One Stage
Understanding Ginject's core architectural pattern where multiple transports share a unified processing model.
Multi Pipelines, One Stage
This is Ginject's defining architectural pattern. It means multiple transports (HTTP, WebSocket, and future protocols) all share the same processing stages. You write your business logic once and it works everywhere.
The Problem It Solves
Traditionally, supporting multiple protocols means building separate applications:
Code duplication is inevitable. Your auth logic is different for HTTP and WebSocket. Middleware works differently. Exceptions are handled differently.
Ginject solves this with one stage model:
How It Works
Pipeline = Transport's Path
A pipeline is how a request from a specific transport flows to the application core:
- HTTP Pipeline: HTTP request → application core → HTTP response
- WebSocket Pipeline: WebSocket event → application core → WebSocket message
Each pipeline has transport-specific mechanics (HTTP status codes vs. event names), but once inside the application, all pipelines merge into the same stages.
Stage = Processing Step
A stage is a processing step that runs for every request, regardless of transport:
- Middleware — logging, request enrichment, short-circuiting
- Guard — authentication and authorization
- Interceptor — pre/post processing, response transformation
- Handler — business logic
- Exception Filter — error handling
Every handler runs through these five stages in the same order. The difference is what data is available and how responses are formatted.
Example: Auth Guard for Both HTTP and WebSocket
The magic of "One Stage" is code reuse. Here's an auth guard that works for both transports:
Now use it in both HTTP and WebSocket controllers:
Result: One guard implementation, both HTTP and WebSocket protected. If you update the auth logic, it updates everywhere automatically.
Why This Matters
1. Code Reuse
Write business logic once:
2. Consistent Architecture
All developers learn one pattern:
- "How do I add auth?" → Use a Guard
- "How do I log requests?" → Use Middleware
- "How do I transform responses?" → Use an Interceptor
This is true for HTTP, WebSocket, and any future transport.
3. Easier Testing
Test your business logic without worrying about HTTP or WebSocket specifics:
4. Easier Scaling
When you need to add a new transport (gRPC, Server-Sent Events, etc.), you get the entire pipeline for free:
Differences Between Transports
While the stages are the same, the data available in each stage differs:
HTTP Handler Example
WebSocket Handler Example
Shared Across Both
Complete Picture
Here's a complete application showing both HTTP and WebSocket sharing the same architecture:
Summary
| Aspect | Traditional | Ginject |
|---|---|---|
| Code organization | Separate HTTP and WS servers | Single application |
| Auth logic | Duplicated for each transport | Shared guard |
| Middleware | Different for each transport | Shared middleware |
| Testing | Test each transport separately | Test once, works everywhere |
| Adding new transport | Reimplement everything | Reuse everything |
The key insight: Your business logic should not care whether the request came from HTTP or WebSocket. Let the framework handle that.