Architecture Deployment API

C4 Level 3 — API Layer Components

Internal structure of the REST API container: how a request flows from controller through the MediatR pipeline into the domain and back.

C4Component
  title REST API — Component View

  Container_Boundary(api, "REST API (.NET 8)") {

    Component(middleware, "Middleware Pipeline", "ASP.NET Core", "GlobalException → RequestId → Localisation → Authentication → TenantResolution → TokenBlacklist → Authorization → RateLimiter → Idempotency → ETag")
    Component(controllers, "Controllers (23)", "ASP.NET Core", "AuthController, BouquetsController, CartController, OrdersController, CustomerBouquetsController, VendorOrdersController, FavouritesController, SubscriptionsController, NotificationsController, ProfileController, SmartVasesController, AnalyticsController, AdminController, EmployeesController, PublicVendorsController, BouquetFeedbackController, StripeWebhookController, ...")
    Component(behaviors, "MediatR Pipeline Behaviours", "MediatR", "ValidationBehavior (FluentValidation), LoggingBehavior, TenantValidationBehavior, CachingBehaviour")
    Component(handlers, "Command/Query Handlers", "MediatR", "35+ handlers — one per command or query, scoped by domain")
    Component(domain, "Domain Aggregates (14)", "DDD", "Vendor, Bouquet, SmartVase, Order, Cart, Customer, Employee, Favourite, Subscription, Notification, BouquetFeedback, DeviceRegistration, SavedAddress, User")
    Component(repos, "Repository Implementations (14)", "EF Core", "One per aggregate — wraps DbContext, enforces tenant scope")
    Component(infra, "Infrastructure Services", ".NET", "KeycloakAdminClient, StripeService, RedisCacheService, MqttDeviceCommunicationService, BouquetHub (SignalR), OutboxDispatcherService, EmailService, TwilioSmsVerificationService, QrCodeGeneratorService, NominatimGeocodingService")
    Component(outbox, "Outbox Pattern", "EF Core + BackgroundService", "EfOutboxWriter (writes on commit) → OutboxDispatcherService (dispatches to InMemoryEventBus)")
  }

  ContainerDb(postgres, "PostgreSQL")
  ContainerDb(redis, "Redis")

  Rel(middleware, controllers, "Passes authenticated + tenant-scoped request")
  Rel(controllers, behaviors, "ISender.Send(command/query)")
  Rel(behaviors, handlers, "Validated + logged + cached request")
  Rel(handlers, domain, "Domain methods, Result(T)")
  Rel(handlers, repos, "IRepository(T) — read/write")
  Rel(handlers, infra, "External service calls")
  Rel(handlers, outbox, "EfOutboxWriter.Write() on domain events")
  Rel(repos, postgres, "EF Core DbContext")
  Rel(infra, redis, "RedisCacheService")
  Rel(outbox, infra, "InMemoryEventBus.Publish()")

Request Lifecycle

sequenceDiagram
  participant Client
  participant MW as Middleware Pipeline
  participant Ctrl as Controller
  participant MediatR
  participant Handler
  participant Domain
  participant Repo
  participant DB as PostgreSQL

  Client->>MW: HTTP Request
  MW->>MW: GlobalException → RequestId → Localisation → Authentication → TenantResolution → TokenBlacklist → Authorization → Idempotency
  MW->>Ctrl: Authenticated + tenant-scoped HttpContext
  Ctrl->>MediatR: ISender.Send(command)
  MediatR->>MediatR: ValidationBehavior (FluentValidation)
  MediatR->>MediatR: LoggingBehavior
  MediatR->>MediatR: TenantValidationBehavior
  MediatR->>Handler: Handle(command, ct)
  Handler->>Repo: GetByIdAsync / FindAsync
  Repo->>DB: SELECT
  DB-->>Repo: Entity
  Repo-->>Handler: Aggregate
  Handler->>Domain: aggregate.DoSomething()
  Domain-->>Handler: Result(T)
  Handler->>Repo: UnitOfWork.SaveChangesAsync()
  Repo->>DB: INSERT/UPDATE + OutboxMessage
  Handler-->>Ctrl: Result(T)
  Ctrl-->>Client: HTTP 200/201/400/409