| Version | Date | Author | Description |
|---|---|---|---|
| 1.0 | 2026-04-11 | Nitish Singh | Initial Architecture & Strategy Pattern Design. |
| 1.1 | 2026-04-14 | Nitish Singh | Added P/Invoke Interop & Docker Orchestration. |
| 1.2 | 2026-04-17 | Nitish Singh | Integrated OpenTelemetry & Trace Context Propagation. |
| 1.3 | 2026-04-19 | Nitish Singh | M2 Hardware Saturation (4 P-Cores) & Batch Logic. |
The Case Conversion API is a multi-layered system that exposes a high-performance C++ string conversion engine through a .NET REST API and serves it to a React-based frontend UI. The architecture separates performance-critical logic from API orchestration and UI concerns.
The system uses native C++ for efficient string transformations, .NET for service exposure, and Docker for containerized deployment.
- Performance: Utilize native C++ for O(n) string transformations.
- Scalability: Saturate M2 Performance Cores via parallel orchestration.
- Observability: Provide full-stack trace propagation across the ABI boundary.
- Security: Enforce a strict 5MB security gate for native memory protection.
- Persistent storage
- Authentication/authorization
- Distributed scaling
- Streaming or batch processing
Frontend (React)
|
v
ASP.NET Core REST API
|
v
P/Invoke Interop Layer
|
v
C++ Shared Library
|
v
Strategy Pattern Conversion EngineResponsibilities:
- Implement conversion strategies
- Dispatch based on user choice
- Return processed string
Design Patterns:
- Strategy Pattern: Each conversion (SnakeCase, LeetSpeak, etc.) is an isolated strategy class.
- Factory Pattern: A central factory dispatches the input string to the correct strategy based on an integer
choice. - Memory Ownership: Follows the "Callee-Allocates, Caller-Frees" contract. The native engine allocates the result on the heap, and .NET is responsible for calling a
freeStringdelegate.
Example Strategies:
- AlternatingCase
- CapitalizeWords
- SnakeCase
- KebabCase
- LeetSpeak
- Reverse
- ToggleCase
Factory selects strategy: Input Choice → Factory → Strategy → Execute
The engine is exposed via a C-compatible interface.
Exported Function:
extern "C" const char* processStringDLL(const char* input, int choice);Responsibilities:
- Bridge C++ and C#
- Dispatch conversion request
- Return C-style string pointer
The Contract: To prevent memory leaks, the system follows a Caller-Must-Cleanup or Static-Return pattern.
Implementation: The C++ engine returns a const char*. The .NET side treats this as an IntPtr.
Safety: Because string conversions are stateless, we avoid long-lived heap allocations in the native layer to minimize the risk of fragmentation.
Uses P/Invoke to call native DLL.
[DllImport("ProcessString")]
private static extern IntPtr processStringDLL(string input, int choice);Responsibilities:
- Marshal strings
- Convert IntPtr to managed string
- Handle API-level validation
Endpoint: POST /api/WordCase/convert
Request: { "input": "hello world", "choice": 1 }
Response: { "output": "hElLo WoRlD" }
Responsibilities:
- Input validation
- Call service layer
- Return JSON response
To maximize throughput on Apple Silicon:
- P-Core Saturation: The system targets the 4 Performance Cores (P-Cores) of the M2.
- Parallel Orchestration: The
convert-batchendpoint usesParallel.ForEachAsyncwith aMaxDegreeOfParallelismof 4. - N+1 Queueing: A
SemaphoreSlimensures that a 5th concurrent request is queued correctly, preventing the system from offloading high-priority work to the slower E-Cores (Efficiency Cores).
Technology:
- React
- TypeScript
- Vite
Responsibilities:
- Collect input
- Provide conversion selection
- Call REST API
- Display results
Stage 1:
- Build C++ shared library
- Publish .NET API
Stage 2:
- Copy artifacts
- Run ASP.NET runtime
User Input (React)
↓
REST API Call
↓
ASP.NET Controller
↓
ProcessStringService
↓
P/Invoke
↓
processStringDLL (C++)
↓
Factory
↓
Strategy
↓
Result
↓
API Response
↓
Frontend Display- Invalid choice → return lower case of string
- Null input → return empty string
- DLL load failure → HTTP 500
- API validation failure → HTTP 400
- Native C++ for string processing
- No heap allocations in critical path
- Static buffer reuse
- Minimal interop overhead
To add new conversion:
- Create new Strategy class
- Implement convert()
- Register in Factory
- Update enum mapping
No API changes required.
C++:
- GoogleTest unit tests
- Strategy validation
.NET:
- Service layer tests
Integration:
- API endpoint tests
Backend: Docker container on port 8080
Frontend: Docker container on port 5173
Docker Compose orchestrates:
- frontend
- backend
- SIMD optimizations
- Async API calls
- Plugin-based strategies
- Benchmark endpoints
- WASM build of C++ engine
C++ — Conversion engine
CMake — Build system
GoogleTest — Unit tests
.NET 8 — REST API
React + TypeScript — UI
Docker — Containerization
GitHub Actions — CI/CDDecision: Use C++ for conversions Reason: Performance and native interop demonstration
Decision: Use P/Invoke Reason: Lightweight interop vs gRPC/native hosting
Decision: Strategy Pattern Reason: Extensible conversion types
Decision: Multi-stage Docker Reason: Smaller runtime image
Decision: Manual Telemetry Propagation Reason: Ensures that errors occurring deep in the C++ engine are correlated to the specific HTTP request that triggered them.
Pros:
- High performance
- Modular design
- Extensible architecture
Cons:
- Native interop complexity
- Memory management risks
- Cross-platform build complexity
To ensure the polyglot boundary is transparent, the system implements:
-
Trace Context Propagation: The .NET Gateway extracts the W3C traceparent and passes it to the native engine.
-
Granular Spans: We track ABI Latency (the time spent converting data between managed and unmanaged memory) separately from Logic Latency (the time the C++ engine takes to run the strategy).
-
Visualizer: All traces are exported via OTLP to a Jaeger/Zipkin backend.
-
Buffer Overflow Protection: The managed API validates the input length against a MAX_BUFFER_SIZE before the P/Invoke call.
-
Sanitization: The C++ engine uses std::string_view or bound-checked iterators to ensure it never reads past the memory allocated by the managed environment.
-
Managed Handling**: The .NET layer detects this sentinel and throws a controlled
ArgumentException, returning a400 Bad Requestto the user instead of crashing the native process.
-
Architecture: Monolithic REST API wrapper over a Native Engine.
-
Focus: Perfecting the ABI boundary and manual memory management.
-
Limit: P(95) latency spikes during Garbage Collection (GC) events under high load.
-
Architecture: Layer 7 Load-Balanced Micro-Cluster.
-
Mechanism: NGINX Reverse Proxy distributing traffic across 4 hardware-optimized replicas.
-
Justification: Necessary to survive the 1,000,000 request endurance test.
-
Result: Stabilized tail latency and 100% success rate by offloading pressure from single-process GC cycles.