An engineering‑focused look at the backend of Claprec, covering its Dockerized N‑Tier design, a bitwise flag system for composing CRUD controllers, service‑layer extensibility, and the real‑time and views‑processing microservices. The article explains why these patterns were chosen, how they affect scalability and consistency, and what compromises were made.
Claprec Backend Deep Dive – N‑Tier, Bitwise CRUD, and Microservice Trade‑offs (2/6)

Problem: Balancing Rapid Delivery with a Clean Architecture
Claprec needed a backend that could evolve quickly, support a growing feature set, and stay maintainable for a team of five engineers. The constraints were:
- Time pressure – the product roadmap demanded new endpoints every sprint.
- Complex domain – many entities share validation, existence checks, and business rules.
- Scalability – the system must handle thousands of concurrent users, real‑time chat, and analytics streams.
- Technology limits – C# does not provide multiple class inheritance, yet the team wanted to reuse common behavior across controllers.
Solution Approach
1. Dockerized N‑Tier Microservices
The backend runs in six containers:
- API – the primary service exposing REST endpoints.
- Real‑time – a SignalR hub for live chat and presence.
- Views‑processing – an event‑driven worker that aggregates view metrics.
- Frontend – Angular SSR container (handled in part 3).
- MSSQL – relational store for core data and enums.
- RabbitMQ – message broker for decoupled communication.
Each microservice follows a classic N‑Tier layout (Controller → Service → Repository). The depth of the stack varies per entity:
| Stack depth | Example layers |
|---|---|
| 8‑layer CRUD | HigherCrudController → BaseHigherCrudController → Service → Validation → BusinessRule → BaseService → Repository |
| 6‑layer Enum | DbEnumController → BaseDbEnumController → Service → BaseService → Repository |
| 4‑layer Generic | Controller → Service → BaseService → Repository |
2. Bitwise Flag System for CRUD Composition
To avoid duplicating code for common CRUD concerns, the team introduced bitwise levels:
- Level 0 – ReadBasic (GET list, GET by id)
- Level 1 – ReadCalculated (adds computed fields)
- Level 2 – Delete (adds DELETE endpoint)
- Level 4 – Create (adds POST with validation)
- Level 8 – Update (adds PATCH with existence checks)
A controller can inherit from a mix class whose numeric value is the sum of the required levels. For example, a full‑CRUD controller inherits from Level 15 (0+1+2+4+8). Because C# lacks multiple inheritance, the mix classes are pre‑chained in a linear hierarchy. The result is an intuitive API for developers: they pick the level they need and the underlying behavior is assembled automatically.
3. Service Layer Mirrors the Controller Hierarchy
Every controller level has a matching service base class. Services expose virtual methods for validation, business rules, and data access, allowing concrete implementations to override only what differs. This mirrors the controller’s composition and keeps the validation pipeline consistent across the board.
4. Validation Pipeline – Format vs. Business Rules
The pipeline runs in two stages:
- Format validation – pure data checks (string length, required fields). Fails fast with 400 Bad Request.
- Business‑rule validation – database‑dependent checks (uniqueness, foreign‑key constraints). Returns 422 Unprocessable Entity.
If the first stage fails, the second is skipped, saving unnecessary DB round‑trips. The validators are built on FluentValidation, wrapped in abstract base classes that throw custom exceptions.
5. Real‑time Microservice (SignalR)
- Memory footprint – ~300 MiB for 5 000 concurrent connections in stress tests.
- Connection tracking – a
ConcurrentDictionarykeyed by user id stores connection ids and target chat ids. - Resilience – state lives in RAM; on a crash the frontend’s auto‑retry reconnects and the service rebuilds its in‑memory map. This trades temporary presence inconsistency for a much simpler implementation.
- Bandwidth optimisation – only unread counts are pushed; full messages are sent only when a user joins a specific chat room.
6. Views‑Processing Microservice (Event‑driven)
- Listens to view events from the main API via RabbitMQ.
- Persists lightweight metadata (entity id, user id, IP, timestamp) in a dedicated MSSQL schema.
- Provides aggregated analytics for dashboards and recommendation engines.
Trade‑offs and Scalability Implications
| Aspect | Benefit | Cost / Consideration |
|---|---|---|
| Bitwise CRUD composition | Eliminates repetitive boilerplate; developers pick a level and get a full set of endpoints automatically. | Requires a library of pre‑generated mix classes; adding a new combination means adding a new class. |
| N‑Tier separation | Clear boundaries make unit testing straightforward; each layer can be swapped (e.g., replace EF with Dapper). | Additional indirection can add latency; careful profiling is needed for high‑throughput paths. |
| In‑memory connection map | Very low latency for presence checks; no external store needed. | State loss on process restart; relies on client reconnection logic to heal. |
| RabbitMQ event bus | Decouples view analytics from the main request path, improving request latency. | Introduces eventual consistency; analytics lag behind real time by a few seconds. |
| Separate enum storage | Enums are versioned in the database, avoiding hard‑coded values in code. | Slightly more complex migration path when adding new enum values. |
Overall, the architecture favours developer velocity and runtime efficiency while accepting modest consistency windows in the analytics pipeline and a small risk of transient presence mismatches.
What Comes Next?
The backend foundation is now solid: a reusable CRUD framework, a scalable real‑time hub, and an analytics worker that off‑loads heavy processing. The next article in the series will shift focus to the frontend – Angular SSR, NgRx state management, and OpenAPI contract‑first integration.
If you have questions about the bitwise controller hierarchy or want to see the source code, check out the GitHub repo linked in the original DEV post.

Comments
Please log in or register to join the discussion