Saltar a contenido

Diagramas de arquitectura del backend

Verificados contra el código de backend/app/ el 2026-10-10. Todos los nombres de módulos, routers, endpoints y transiciones son los reales.

1. Mapa de módulos hexagonales

Los 16 módulos de backend/app/modules/ siguen la estructura interface / application / domain / infrastructure (verificado en chat, comanda y payment). No hay imports cross-module (ADR-004): se comunican por puertos y eventos de dominio. app/shared/ contiene código transversal, app/core/ el bootstrap y app/worker/ el runner recompute-recs.

flowchart TB
    subgraph MOD["app/modules/ (16 módulos hexagonales)"]
        direction LR
        auth --- session
        session --- table
        table --- menu
        menu --- chat
        chat --- comanda
        comanda --- payment
        payment --- customer
        customer --- restaurant
        restaurant --- review
        review --- recommendation
        recommendation --- analytics
        analytics --- manager
        manager --- admin_user
        admin_user --- audit
        audit --- errors
    end
    CORE["app/core/<br/>config · database · deps<br/>event_wiring · permissions"]
    SHARED["app/shared/<br/>domain · application<br/>infrastructure · identity"]
    WORKER["app/worker/runner.py<br/>recompute-recs"]
    API["app/api/ + app/main.py<br/>montaje de routers"]
    MOD -.->|puertos / eventos| SHARED
    MOD --> API
    CORE --> MOD
    CORE --> API
    WORKER -->|usa| MOD
    WORKER -->|usa| SHARED

2. State machine de comanda

Fuente de verdad: comanda/domain/state_machine.py (ComandaStateMachine, match/case exhaustivo). Existe un duplicado legacy en comanda/infrastructure/comanda_service.py (VALID_TRANSITIONS) que es más restrictivo (ver nota).

stateDiagram-v2
    [*] --> DRAFT
    DRAFT --> AI_REVIEWING
    DRAFT --> PENDING : modo none RD04
    DRAFT --> CANCELED
    AI_REVIEWING --> AI_REJECTED
    AI_REVIEWING --> PENDING_MANAGER : modo full
    AI_REVIEWING --> PENDING : modo ai_only RD04
    AI_REVIEWING --> CANCELED
    AI_REJECTED --> DRAFT
    AI_REJECTED --> CANCELED
    PENDING_MANAGER --> MANAGER_REJECTED
    PENDING_MANAGER --> PENDING : aprobada
    PENDING_MANAGER --> CANCELED
    MANAGER_REJECTED --> DRAFT
    MANAGER_REJECTED --> CANCELED
    PENDING --> IN_PROGRESS
    PENDING --> CANCELED
    IN_PROGRESS --> READY
    IN_PROGRESS --> SERVED : entrega directa
    IN_PROGRESS --> CANCELED
    READY --> SERVED
    READY --> CANCELED
    SERVED --> CLOSED
    CLOSED --> [*]

Nota: VALID_TRANSITIONS (infraestructura) no incluye los atajos RD04 DRAFT→PENDING ni AI_REVIEWING→PENDING, que sí existen en el dominio. El diagrama dibuja el dominio, que es la regla vigente.

3. Flujo de pago

Router payment/interface/router.py (prefijo /sessions): POST /{token}/bill (request), GET /{token}/bill (get), PATCH /{token}/bill (configure split+tip), POST /{token}/bill/payments (pay, con header Idempotency-Key, TTL 24h). Use cases en payment/application/use_cases/ y FakePaymentGateway en payment/infrastructure/gateways/fake_payment_gateway.py.

sequenceDiagram
    participant C as Cliente
    participant R as payment/router.py<br/>(prefix /sessions)
    participant I as PaymentIdempotencyStore<br/>(claim/complete)
    participant U as Use cases<br/>Request/Get/Configure/PayBill
    participant G as FakePaymentGateway<br/>IPaymentGateway
    participant B as SqlAlchemyBillRepository
    C->>R: POST /{token}/bill (request_bill)
    R->>U: RequestBillUseCase.execute()
    U->>B: crea o refresca bill
    C->>R: GET /{token}/bill (get_bill)
    R->>U: GetBillUseCase.execute()
    C->>R: PATCH /{token}/bill (split_mode + tip)
    R->>U: ConfigureBillUseCase.execute()
    Note over R,U: 422 tip inválido · 400 split inválido<br/>404 sin bill · 409 bill PAID
    C->>R: POST /{token}/bill/payments + Idempotency-Key
    R->>I: claim(scope=pay_bill, key)
    alt duplicado en TTL
        I-->>R: respuesta cacheada (replay)
    else primera vez
        R->>U: PayBillUseCase.execute()
        U->>G: charge(amount_cents)
        G-->>U: ref / error FAIL-CLOSED 502
        U->>B: registra pago (402 si excede resto)
        R->>I: complete(scope, key, 201, body)
    end
    R-->>C: 201 PayBillResponse + bill

4. Flujo QR → sesión → carrito → comanda

Routers reales: table/interface/qr_router.py (/admin/qr, POST /generate, POST /validate), session/interface/sessions_router.py (prefijo /sessions: POST /peek, POST /validate, GET /{token}, PATCH /{token}/guest-count), session/interface/cart_router.py (prefijo /sessions: GET /{token}/cart, POST /{token}/cart/items, PATCH/DELETE .../items/{item_id}), comanda/interface/customer_router.py (prefijo /sessions: GET /{token}/draft, POST /{token}/lines, POST /{token}/submit, POST /{token}/cancel).

sequenceDiagram
    participant M as Mesa QR (JWT HMAC)
    participant T as table/qr + public_tables<br/>admin/qr + GET /tables/{qr}
    participant S as session/sessions_router<br/>POST /sessions/validate
    participant K as session/cart_router<br/>/sessions/{token}/cart
    participant O as comanda/customer_router<br/>/sessions/{token}/...
    M->>T: escanea QR → POST /admin/qr/validate
    T-->>M: table_id + restaurant_id
    M->>S: POST /sessions/validate (qr_jwt, guest_count)
    Note over S: idempotente natural:<br/>reintento reusa session_token<br/>is_new_session=false
    S-->>M: session_token + expires_at
    M->>K: POST /{token}/cart/items + Idempotency-Key
    K-->>M: 201 CartItemResponse (id UUID str)
    M->>K: PATCH /{token}/cart/items/{id} (cantidad)
    M->>O: POST /{token}/lines (carrito → draft)
    O-->>M: Comanda DRAFT
    M->>O: POST /{token}/submit (revisión IA/manager)
    M->>O: GET /{token}/ticket · GET /{token}/tab

5. Capas hexagonales de un módulo (comanda)

Ejemplo con ficheros reales de backend/app/modules/comanda/. Las dependencias apuntan siempre hacia dentro: interface → application → domain ← infrastructure.

flowchart LR
    subgraph INT["interface/"]
        CR["customer_router.py<br/>GET draft · POST lines/submit"]
        AR["admin_orders_router.py"]
        VR["order_validation_router.py"]
    end
    subgraph APP["application/"]
        UC["use_cases/"]
        CMD["commands.py · serializers.py"]
    end
    subgraph DOM["domain/"]
        SM["state_machine.py<br/>ComandaStateMachine"]
        EN["entities.py · value_objects.py<br/>ComandaStatus"]
        PT["ports.py · repository.py"]
    end
    subgraph INF["infrastructure/"]
        SVC["comanda_service.py<br/>VALID_TRANSITIONS legacy"]
        REPO["repositories/ · orm.py · models/"]
        SUB["subscribers.py"]
    end
    CR --> UC
    AR --> UC
    VR --> UC
    UC --> CMD
    UC --> PT
    CMD --> EN
    REPO -.->|implementa| PT
    SVC -.->|usa| SM
    SUB -.->|escucha| DOM