|
| 1 | +# 3. C4 System |
| 2 | + |
| 3 | +Date: 2025-11-25 |
| 4 | + |
| 5 | +## Status |
| 6 | + |
| 7 | +Accepted |
| 8 | + |
| 9 | +## Context |
| 10 | + |
| 11 | +The decision to implement the C4 model (Context, Containers, Components, Code) in our BFF service architecture is driven |
| 12 | +by the need for a comprehensive, structured, and scalable approach to documenting and communicating |
| 13 | +the software architecture. The C4 model is renowned for its effectiveness in providing various levels of abstraction, |
| 14 | +which can be understood by different stakeholders ranging from developers to business personnel. |
| 15 | + |
| 16 | +The BFF service is built using **Go (Golang)** and leverages several key technologies: |
| 17 | + |
| 18 | +- **chi router** for HTTP routing and middleware |
| 19 | +- **oapi-codegen** for OpenAPI 3.0 code generation |
| 20 | +- **Redis** for caching to improve performance |
| 21 | +- **Ory Kratos** for authentication |
| 22 | +- **SpiceDB** for authorization and permission checks |
| 23 | +- **OpenTelemetry** for distributed tracing and observability |
| 24 | +- **gRPC** for communication with backend services |
| 25 | + |
| 26 | +Implementing the C4 model will enable us to: |
| 27 | + |
| 28 | +- Clearly define the high-level context of the BFF service, including key system interactions and integrations. |
| 29 | +- Break down the service into containers (HTTP API, controllers, etc.), illustrating the overall system architecture. |
| 30 | +- Detail out components within each container, clarifying internal architectures. |
| 31 | +- Optionally, delve into the code level to understand how components are implemented. |
| 32 | + |
| 33 | +This approach is expected to streamline our documentation process, |
| 34 | +improve communication about system architecture within the team, and facilitate onboarding of new team members. |
| 35 | + |
| 36 | +## Decision |
| 37 | + |
| 38 | +We will focus specifically on creating **System Context**, **Container**, and **Component Diagrams** as part of the C4 model implementation. |
| 39 | +These diagrams will detail the internal structure and interactions of components within the BFF service, |
| 40 | +providing a clear view of how various parts of the system work together. |
| 41 | + |
| 42 | +### System context diagram |
| 43 | + |
| 44 | +```plantuml |
| 45 | +@startuml C4_Context_Diagram_for_BFF_Service |
| 46 | +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml |
| 47 | +
|
| 48 | +LAYOUT_WITH_LEGEND() |
| 49 | +
|
| 50 | +title System Context Diagram for BFF Service |
| 51 | +
|
| 52 | +Person(user, "User", "An end user who interacts with the web application to manage short links.") |
| 53 | +System_Ext(web_app, "Web Application", "Next.js/React", "Frontend web application that provides UI for managing short links.") |
| 54 | +System_Ext(kratos, "Ory Kratos", "Identity Provider", "Handles user authentication, registration, and identity management.") |
| 55 | +System_Ext(spicedb, "SpiceDB", "Authorization Service", "Handles permission checks using Zanzibar-style fine-grained authorization.") |
| 56 | +System_Boundary(link_boundary, "Link Boundary Context") { |
| 57 | + System(link_service, "Link Service", "Domain Service", "Core service for managing short links and link operations.") |
| 58 | + System(metadata_service, "Metadata Service", "Domain Service", "Service for fetching and storing link metadata and screenshots.") |
| 59 | +} |
| 60 | +System_Boundary(bff_boundary, "BFF Boundary") { |
| 61 | + System(bff_service, "BFF Service", "API Gateway", "Backend-for-Frontend service that aggregates and transforms data for the web application.") |
| 62 | +} |
| 63 | +
|
| 64 | +Rel(user, web_app, "Uses", "HTTPS") |
| 65 | +Rel(web_app, bff_service, "Makes API requests", "HTTPS/REST") |
| 66 | +Rel(bff_service, kratos, "Validates user sessions", "HTTPS") |
| 67 | +Rel(bff_service, spicedb, "Checks user permissions", "gRPC") |
| 68 | +Rel(bff_service, link_service, "Proxies link operations", "gRPC") |
| 69 | +Rel(bff_service, metadata_service, "Queries metadata", "gRPC") |
| 70 | +
|
| 71 | +' Interaction notes |
| 72 | +note right of kratos |
| 73 | + **Ory Kratos** |
| 74 | + Only user session validation |
| 75 | + - Validates authentication tokens |
| 76 | + - Checks if user is logged in |
| 77 | + - Provides user identity information |
| 78 | +end note |
| 79 | +
|
| 80 | +note right of spicedb |
| 81 | + **SpiceDB** |
| 82 | + Object-level permissions |
| 83 | + - "can read link" |
| 84 | + - "can modify link" |
| 85 | + - Fine-grained authorization |
| 86 | + - Zanzibar-style permissions |
| 87 | +end note |
| 88 | +
|
| 89 | +' Use Cases |
| 90 | +note right of bff_service |
| 91 | + Use Cases: |
| 92 | + 1. Manage links (CRUD operations) |
| 93 | + 2. CQRS operations for links |
| 94 | + 3. Sitemap operations |
| 95 | +end note |
| 96 | +
|
| 97 | +@enduml |
| 98 | +``` |
| 99 | + |
| 100 | +### Container diagram |
| 101 | + |
| 102 | +```plantuml |
| 103 | +@startuml Container_Diagram_for_BFF_Service |
| 104 | +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml |
| 105 | +
|
| 106 | +LAYOUT_WITH_LEGEND() |
| 107 | +
|
| 108 | +title Container Diagram for BFF Service |
| 109 | +
|
| 110 | +Person(web_client, "Web Client", "A web application user interacting with the system.") |
| 111 | +System_Ext(kratos, "Ory Kratos", "Handles user authentication and identity management.") |
| 112 | +System_Ext(spicedb, "SpiceDB", "Handles permission checks using Zanzibar-style authorization.") |
| 113 | +System_Ext(link_service, "Link Service", "Service for managing short links.") |
| 114 | +System_Ext(link_command, "Link Command Service", "CQRS command operations.") |
| 115 | +System_Ext(link_query, "Link Query Service", "CQRS query operations.") |
| 116 | +System_Ext(sitemap_service, "Sitemap Service", "Handles sitemap operations.") |
| 117 | +
|
| 118 | +System_Boundary(bff_system, "BFF System") { |
| 119 | + Container(bff_api, "BFF API", "Go/HTTP", "HTTP REST API server using chi router.") |
| 120 | + ContainerDb(cache, "Redis Cache", "Redis", "Caches frequently accessed data to improve performance.") |
| 121 | +} |
| 122 | +
|
| 123 | +Rel(web_client, bff_api, "Makes requests", "HTTPS/REST") |
| 124 | +Rel(bff_api, kratos, "Authenticates via", "HTTPS") |
| 125 | +Rel(bff_api, spicedb, "Checks permissions via", "gRPC") |
| 126 | +Rel(bff_api, cache, "Reads/Writes cached data", "Redis Protocol") |
| 127 | +Rel(bff_api, link_service, "Proxies requests to", "gRPC") |
| 128 | +Rel(bff_api, link_command, "Sends commands to", "gRPC") |
| 129 | +Rel(bff_api, link_query, "Sends queries to", "gRPC") |
| 130 | +Rel(bff_api, sitemap_service, "Proxies requests to", "gRPC") |
| 131 | +
|
| 132 | +@enduml |
| 133 | +``` |
| 134 | + |
| 135 | +### Component diagram |
| 136 | + |
| 137 | +```plantuml |
| 138 | +@startuml Component_Diagram_for_BFF_Service |
| 139 | +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml |
| 140 | +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml |
| 141 | +
|
| 142 | +LAYOUT_WITH_LEGEND() |
| 143 | +
|
| 144 | +title Component Diagram for BFF Service |
| 145 | +
|
| 146 | +Person(web_client, "Web Client", "A web application user.") |
| 147 | +System_Ext(kratos, "Ory Kratos", "Handles user authentication and identity management.") |
| 148 | +System_Ext(spicedb, "SpiceDB", "Handles permission checks using Zanzibar-style authorization.") |
| 149 | +System_Ext(link_service, "Link Service", "Manages short links.") |
| 150 | +System_Ext(link_command, "Link Command Service", "CQRS command operations.") |
| 151 | +System_Ext(link_query, "Link Query Service", "CQRS query operations.") |
| 152 | +System_Ext(sitemap_service, "Sitemap Service", "Handles sitemap operations.") |
| 153 | +ContainerDb_Ext(cache, "Redis Cache", "Redis", "Caches frequently accessed data.") |
| 154 | +
|
| 155 | +System_Boundary(bff_system, "BFF System") { |
| 156 | + Container(bff_api, "BFF API", "Go/HTTP", "HTTP REST API server using chi router.") |
| 157 | +
|
| 158 | + Container_Boundary(interface_layer, "Interface Layer") { |
| 159 | + Container_Boundary(rest_handlers, "REST Handlers") { |
| 160 | + Component(link_controller, "Link Controller", "Go", "Handles link CRUD operations and data transformation.") |
| 161 | + Component(cqrs_controller, "CQRS Controller", "Go", "Handles CQRS operations for links with data aggregation.") |
| 162 | + Component(sitemap_controller, "Sitemap Controller", "Go", "Handles sitemap operations.") |
| 163 | + } |
| 164 | + Container_Boundary(middleware, "Middleware Stack") { |
| 165 | + Component(tracing_middleware, "Tracing Middleware", "Go", "Handles distributed tracing (1st in pipeline).") |
| 166 | + Component(logger_middleware, "Logger Middleware", "Go", "Logs HTTP requests and responses (2nd in pipeline).") |
| 167 | + Component(metrics_middleware, "Metrics Middleware", "Go", "Collects metrics and observability data (3rd in pipeline).") |
| 168 | + Component(csrf_middleware, "CSRF Middleware", "Go", "Protects against CSRF attacks (4th in pipeline).") |
| 169 | + Component(auth_middleware, "Auth Middleware", "Go", "Validates authentication tokens via Ory Kratos (5th in pipeline).") |
| 170 | + Component(permission_middleware, "Permission Middleware", "Go", "Checks permissions via SpiceDB (6th in pipeline).") |
| 171 | + } |
| 172 | + } |
| 173 | +
|
| 174 | + Container_Boundary(application_layer, "Application Layer") { |
| 175 | + Component(aggregation_service, "Aggregation Service", "Go", "Aggregates data from multiple backend services.") |
| 176 | + Component(adapter_service, "Adapter Service", "Go", "Transforms and adapts data between frontend and backend formats.") |
| 177 | + } |
| 178 | +
|
| 179 | + Container_Boundary(infrastructure_layer, "Infrastructure Layer") { |
| 180 | + Component(cache_component, "Redis Client", "Go", "Manages Redis cache operations for performance optimization.") |
| 181 | + Component(grpc_link_client, "gRPC Link Client", "Go", "Client for Link Service gRPC communication.") |
| 182 | + Component(grpc_command_client, "gRPC Command Client", "Go", "Client for Link Command Service gRPC communication.") |
| 183 | + Component(grpc_query_client, "gRPC Query Client", "Go", "Client for Link Query Service gRPC communication.") |
| 184 | + Component(grpc_sitemap_client, "gRPC Sitemap Client", "Go", "Client for Sitemap Service gRPC communication.") |
| 185 | + Component(error_transformer, "Error Transformer", "Go", "Transforms domain and gRPC errors into structured JSON responses.") |
| 186 | + } |
| 187 | +
|
| 188 | + Container_Boundary(cross_cutting, "Cross-Cutting Concerns") { |
| 189 | + Container_Boundary(observability, "Observability") { |
| 190 | + Component(logger_middleware, "Logger Middleware", "Go", "Logs HTTP requests and responses.") |
| 191 | + Component(metrics_middleware, "Metrics Middleware", "Go", "Collects metrics and observability data.") |
| 192 | + Component(tracing_middleware, "Tracing Middleware", "Go", "Handles distributed tracing.") |
| 193 | + Component(profiling_component, "Profiling Component", "Go", "Provides pprof endpoints for performance profiling.") |
| 194 | + Component(flight_trace_component, "Flight Trace", "Go", "Records request traces for debugging and analysis.") |
| 195 | + } |
| 196 | + Component(i18n_component, "i18n Component", "Go", "Provides internationalization support (en-GB, de-DE, fr-CH).") |
| 197 | + } |
| 198 | +} |
| 199 | +
|
| 200 | +Rel(web_client, bff_api, "Makes requests", "HTTPS/REST") |
| 201 | +Rel(bff_api, tracing_middleware, "Processes through", "1. Request pipeline") |
| 202 | +Rel(tracing_middleware, logger_middleware, "Processes through", "2. Request pipeline") |
| 203 | +Rel(logger_middleware, metrics_middleware, "Processes through", "3. Request pipeline") |
| 204 | +Rel(metrics_middleware, csrf_middleware, "Processes through", "4. Request pipeline") |
| 205 | +Rel(csrf_middleware, auth_middleware, "Processes through", "5. Request pipeline") |
| 206 | +Rel(auth_middleware, permission_middleware, "Processes through", "6. Request pipeline") |
| 207 | +Rel(permission_middleware, link_controller, "Routes to", "After middleware") |
| 208 | +Rel(permission_middleware, cqrs_controller, "Routes to", "After middleware") |
| 209 | +Rel(permission_middleware, sitemap_controller, "Routes to", "After middleware") |
| 210 | +Rel(auth_middleware, kratos, "Validates tokens", "HTTPS") |
| 211 | +Rel(permission_middleware, spicedb, "Checks permissions", "gRPC") |
| 212 | +
|
| 213 | +' Middleware order note |
| 214 | +note right of middleware |
| 215 | + **Middleware Pipeline Order:** |
| 216 | + 1. Tracing (captures full request context) |
| 217 | + 2. Logger (includes trace-id in logs) |
| 218 | + 3. Metrics (includes user-id in metrics) |
| 219 | + 4. CSRF (security validation) |
| 220 | + 5. Auth (user session validation) |
| 221 | + 6. Permission (object-level checks) |
| 222 | +
|
| 223 | + This order ensures trace-id and user-id |
| 224 | + are available in all observability data. |
| 225 | +end note |
| 226 | +
|
| 227 | +' Security interaction notes |
| 228 | +note right of auth_middleware |
| 229 | + **Ory Kratos Interaction:** |
| 230 | + Only user session validation |
| 231 | + - Validates authentication tokens |
| 232 | + - Checks if user is logged in |
| 233 | + - Provides user identity information |
| 234 | +end note |
| 235 | +
|
| 236 | +note right of permission_middleware |
| 237 | + **SpiceDB Interaction:** |
| 238 | + Object-level permissions |
| 239 | + - "can read link" |
| 240 | + - "can modify link" |
| 241 | + - Fine-grained authorization |
| 242 | + - Zanzibar-style permissions |
| 243 | +end note |
| 244 | +Rel(link_controller, aggregation_service, "Uses for data aggregation") |
| 245 | +Rel(cqrs_controller, aggregation_service, "Uses for data aggregation") |
| 246 | +Rel(sitemap_controller, adapter_service, "Uses for data transformation") |
| 247 | +Rel(aggregation_service, cache_component, "Uses for caching", "Redis Protocol") |
| 248 | +Rel(adapter_service, cache_component, "Uses for caching", "Redis Protocol") |
| 249 | +Rel(cache_component, cache, "Reads/Writes", "Redis Protocol") |
| 250 | +Rel(aggregation_service, grpc_link_client, "Uses", "gRPC") |
| 251 | +Rel(aggregation_service, grpc_command_client, "Uses", "gRPC") |
| 252 | +Rel(aggregation_service, grpc_query_client, "Uses", "gRPC") |
| 253 | +Rel(adapter_service, grpc_sitemap_client, "Uses", "gRPC") |
| 254 | +Rel(grpc_link_client, link_service, "Calls", "gRPC") |
| 255 | +Rel(grpc_command_client, link_command, "Calls", "gRPC") |
| 256 | +Rel(grpc_query_client, link_query, "Calls", "gRPC") |
| 257 | +Rel(grpc_sitemap_client, sitemap_service, "Calls", "gRPC") |
| 258 | +Rel(link_controller, error_transformer, "Uses for error transformation") |
| 259 | +Rel(cqrs_controller, error_transformer, "Uses for error transformation") |
| 260 | +Rel(sitemap_controller, error_transformer, "Uses for error transformation") |
| 261 | +Rel(bff_api, i18n_component, "Uses for localization") |
| 262 | +Rel(bff_api, profiling_component, "Exposes via", "/debug/pprof") |
| 263 | +Rel(bff_api, flight_trace_component, "Uses for debugging") |
| 264 | +
|
| 265 | +@enduml |
| 266 | +``` |
| 267 | + |
| 268 | +## Consequences |
| 269 | + |
| 270 | +### Benefits |
| 271 | + |
| 272 | +- **Improved understanding and communication**: Clear visualization of BFF service architecture helps all stakeholders understand the system design. |
| 273 | +- **Increased efficiency**: Well-documented architecture reduces time spent on understanding system interactions during development and maintenance. |
| 274 | +- **Better integration clarity**: Clear visualization of how the BFF service integrates with Ory Kratos, SpiceDB, and backend services. |
| 275 | +- **Enhanced onboarding**: New team members can quickly understand the system structure through visual diagrams. |
| 276 | +- **Facilitated discussions**: Architecture diagrams serve as a common reference point for technical discussions and decision-making. |
| 277 | + |
| 278 | +### Technical Details |
| 279 | + |
| 280 | +The diagrams illustrate: |
| 281 | + |
| 282 | +- **Request flow**: From web client through middleware stack to controllers and backend services. |
| 283 | +- **Security layers**: Authentication (Ory Kratos) and authorization (SpiceDB) integration points. |
| 284 | +- **Performance optimization**: Redis caching strategy for frequently accessed data. |
| 285 | +- **Observability**: Metrics, tracing, profiling, and flight trace components for monitoring and debugging. |
| 286 | +- **Error handling**: Structured error transformation from domain/gRPC errors to client-friendly JSON responses. |
| 287 | +- **Internationalization**: Multi-language support (en-GB, de-DE, fr-CH) for localized responses. |
0 commit comments