# BAGIAN 4: FLOWCHART BACKEND (BE)
## Detailed Backend Application Flowcharts

---

## 4.1 BE General Service Architecture

Arsitektur umum Backend menggunakan pattern **Repository → Service → Route** (Layered Architecture).

```mermaid
flowchart TD
    ENTRY["index.ts<br/>Bun HTTP Server<br/>Port: 8000"] --> APP_SVC["app.service.ts<br/>createApp()"]
    APP_SVC --> INIT_PUSH["Initialize Pusher"]
    APP_SVC --> INIT_RMQ["Initialize RabbitMQ"]
    APP_SVC --> DEV_INIT{"Development?"}
    DEV_INIT -->|Yes| DB_INIT["Initialize DB<br/>Test Connection"]

    subgraph GLOBAL_MW["Global Middleware Stack"]
        GM1["CORS (cors.config.ts)<br/>Allowed origins validation"]
        GM2["Timing (response time header)"]
        GM3["Request ID (unique per request)"]
        GM4["Logger (request logging)"]
        GM5["Body Limit (5MB default)"]
        GM6["Timeout (5min default)"]
        GM1 --> GM2 --> GM3 --> GM4 --> GM5 --> GM6
    end

    APP_SVC --> GLOBAL_MW

    subgraph ROUTES["Route Registration (v1.ts)"]
        R_AUTH["/api/v1/auth/*<br/>better-auth handler"]
        R_WEBHOOK["/api/webhook/*<br/>Webhook receivers"]
        R_PROTECTED["/api/v1/u/*<br/>requireAuth middleware"]
        R_UPLOADS["/api/uploads<br/>File storage"]
        R_SWAGGER["/api/v1/swagger<br/>OpenAPI docs"]
    end

    GLOBAL_MW --> ROUTES

    subgraph AUTH_MW_LAYER["Auth Middleware Chain"]
        AM1["requireAuth<br/>(Session verification)"]
        AM2["optionalAuth<br/>(No block if missing)"]
        AM3["requirePermission<br/>(RBAC check)"]
        AM4["requireWorkspaceApiKey<br/>(x-api-key header)"]
        AM5["requireAuthOrWorkspaceApiKey<br/>(Either session or API key)"]
    end

    R_PROTECTED --> AUTH_MW_LAYER

    AUTH_MW_LAYER --> SERVICE["Service Layer<br/>(47 Modules)"]
    SERVICE --> REPO["Repository Layer<br/>(Drizzle ORM queries)"]
    REPO --> DB["PostgreSQL<br/>schema: omnichannel<br/>30+ tables"]

    SERVICE --> NOTIFY["Real-time Notification<br/>notifyHybrid()<br/>Socket.IO + Pusher"]
    SERVICE --> EXT_API["External APIs<br/>WhatsApp / Instagram / Telegram"]

    R_WEBHOOK --> RABBIT["RabbitMQ Publisher"]
    RABBIT --> WORKER["Worker Consumer"]
    WORKER --> SERVICE

    style ENTRY fill:#C8E6C9,stroke:#4CAF50
    style GLOBAL_MW fill:#FFF3E0,stroke:#FF9800
    style AUTH_MW_LAYER fill:#FFEBEE,stroke:#F44336
    style SERVICE fill:#E3F2FD,stroke:#1976D2
    style REPO fill:#F3E5F5,stroke:#9C27B0
```

---

## 4.2 BE Auth & RBAC Flow (Detailed)

Alur lengkap autentikasi dan authorization di Backend.

```mermaid
flowchart TD
    REQUEST["Incoming Request"] --> ROUTE_TYPE{Route Type?}

    subgraph PUBLIC_ROUTES["Public Routes (No Auth)"]
        ROUTE_TYPE -->|"/api/v1/swagger"| SWAGGER["Swagger UI"]
        ROUTE_TYPE -->|"/api/webhook/whatsapp<br/>(GET verify)"| WH_VERIFY["Webhook Verification"]
    end

    subgraph BETTER_AUTH["Better-Auth Self-Handled"]
        ROUTE_TYPE -->|"/api/v1/auth/*"| BA_HANDLER["better-auth handler"]
        BA_HANDLER --> BA_SIGNIN["sign-in/email-password"]
        BA_HANDLER --> BA_SIGNUP["sign-up/email-password"]
        BA_HANDLER --> BA_FORGET["forget-password"]
        BA_HANDLER --> BA_RESET["reset-password"]
        BA_HANDLER --> BA_CHANGE["change-password"]
        BA_HANDLER --> BA_SESSION["get-session"]
        BA_HANDLER --> BA_ADMIN["admin/list-users"]
        BA_HANDLER --> BA_CREATE["admin/create-user"]
        BA_HANDLER --> BA_UPDATE["admin/update-user"]
        BA_HANDLER --> BA_SET_ROLE["admin/set-role"]
        BA_HANDLER --> BA_UNBAN["admin/unban-user"]
        BA_HANDLER --> BA_ORG_LIST["organization/list-members"]
        BA_HANDLER --> BA_ORG_INVITE["organization/invite-member"]
        BA_HANDLER --> BA_ORG_UPDATE["organization/update-member-role"]
        BA_HANDLER --> BA_ORG_REMOVE["organization/remove-member"]
        BA_HANDLER --> BA_ORG_ACCEPT["organization/accept-invitation"]
        BA_HANDLER --> BA_APIKEY["api-key/create"]
    end

    subgraph PROTECTED["Protected Routes (Auth Required)"]
        ROUTE_TYPE -->|"/api/v1/u/*"| AUTH_CHECK

        AUTH_CHECK["Which Auth Method?"]
        AUTH_CHECK -->|"Default"| REQUIRE_AUTH["requireAuth middleware"]
        AUTH_CHECK -->|"Tickets"| EITHER["requireAuthOrWorkspaceApiKey"]
        AUTH_CHECK -->|"Optional"| OPTIONAL["optionalAuth"]

        REQUIRE_AUTH --> SESSION_CHECK{Valid Session<br/>Cookie?}
        SESSION_CHECK -->|No| ERR_401["401 Unauthorized<br/>response.util.ts"]
        SESSION_CHECK -->|Yes| SET_CTX["Set c.set('user', user)<br/>c.set('session', session)"]

        EITHER --> CHECK_EITHER{Session OR<br/>API Key?}
        CHECK_EITHER -->|Neither| ERR_401
        CHECK_EITHER -->|Session| SET_CTX
        CHECK_EITHER -->|API Key| CHECK_API{Valid API Key?<br/>x-api-key header}
        CHECK_API -->|No| ERR_401
        CHECK_API -->|Yes| SET_API_CTX["Set user + workspace<br/>from key metadata"]

        SET_CTX --> RBAC_CHECK
        SET_API_CTX --> RBAC_CHECK

        RBAC_CHECK["requirePermission({resource: action})"]
        RBAC_CHECK --> CHECK_ROLE{User has<br/>permission?}
        CHECK_ROLE -->|No| ERR_403["403 Forbidden"]
        CHECK_ROLE -->|Yes| HANDLER["Route Handler"]
    end

    HANDLER --> SVC["Service → Repository → DB"]
    SVC --> RESP["Response<br/>response.util.ts<br/>success / error / paginated"]
    SVC --> NOTIFY["notifyHybrid()<br/>Pusher + Socket.IO"]

    style PUBLIC_ROUTES fill:#E8F5E9,stroke:#4CAF50
    style BETTER_AUTH fill:#FFF3E0,stroke:#FF9800
    style PROTECTED fill:#FFEBEE,stroke:#F44336
```

---

## 4.3 BE Conversation & Message Flow (Chat Engine)

Alur lengkap mesin chat Backend, baik inbound maupun outbound.

```mermaid
flowchart TD
    subgraph INBOUND["INBOUND MESSAGE FLOW"]
        WEBHOOK["Webhook Received"] --> PARSE["Parse Platform Data"]

        PARSE --> EXTRACT["Extract:<br/>sender_phone, content, type,<br/>channel_code, raw_id"]
        EXTRACT --> PROFANITY{"Profanity<br/>Filter?"}
        PROFANITY -->|Flagged| MARK_FLAGGED["Mark as flagged"]
        PROFANITY -->|Clean| FIND_CONTACT

        MARK_FLAGGED --> FIND_CONTACT{Contact<br/>Exists?<br/>MATCH by phone + channel_id}

        FIND_CONTACT -->|No| CREATE_CONTACT["Create Contact<br/>INSERT contacts<br/>contact_name=phone<br/>contact_phone=phone"]
        FIND_CONTACT -->|Yes| GET_CONTACT["SELECT contact<br/>WHERE phone + channel_id"]

        CREATE_CONTACT --> FIND_CONV
        GET_CONTACT --> FIND_CONV

        FIND_CONV{Active Conversation?<br/>MATCH by contact_id<br/>+ channel_id<br/>+ status NOT CLOSED/RESOLVED}
        FIND_CONV -->|No| CREATE_CONV["INSERT conversations<br/>status: UNASSIGNED<br/>unread_counts: 1"]
        FIND_CONV -->|Yes Chain| UPDATE_CHAIN["Link previous_conversation_id"]
        FIND_CONV -->|Yes Active| UPDATE_CONV

        UPDATE_CHAIN --> UPDATE_CONV["UPDATE conversations<br/>last_message, last_message_at<br/>unread_counts++<br/>conversation_24h_window<br/>(WhatsApp only)"]

        UPDATE_CONV --> SAVE_MSG["INSERT conversation_messages<br/>from=customer, to=system<br/>type based on content"]

        SAVE_MSG --> SAVE_ATTACH{"Has<br/>Attachments?"}
        SAVE_ATTACH -->|Yes| SAVE_ATT["INSERT message_attachments<br/>Download media from<br/>platform API"]
        SAVE_ATTACH -->|No| CHECK_OH
        SAVE_ATT --> CHECK_OH

        CHECK_OH{"Within<br/>Office Hours?<br/>office-hours-schedule"}
        CHECK_OH -->|No| AUTO_RESP["Send Auto Response<br/>office-hours-auto-responses<br/>→ Platform Send API"]
        CHECK_OH -->|Yes| NOTIFY

        AUTO_RESP --> NOTIFY["notifyHybrid()"]
        NOTIFY --> P1["Pusher: private-room:{convId}<br/>Event: new-message"]
        NOTIFY --> P2["Pusher: private-channel:{chId}<br/>Event: new-conversation<br/>(if new)"]
        NOTIFY --> S1["Socket.IO: emit new-message"]
    end

    subgraph OUTBOUND["OUTBOUND MESSAGE FLOW"]
        SEND_API["POST /conversation-messages<br/>(Agent sends message)"] --> VALIDATE["Validate with Zod<br/>conversation-message.validation.ts"]

        VALIDATE --> CHECK_TYPE{Message Type?}
        CHECK_TYPE -->|"TEXT"| PREPARE_TEXT["Prepare text content"]
        CHECK_TYPE -->|"IMAGE/VIDEO/AUDIO/DOC"| UPLOAD_MEDIA["Check media<br/>Already uploaded or new"]
        CHECK_TYPE -->|"TEMPLATE"| PREPARE_TEMPL["Prepare template<br/>components + variables"]

        PREPARE_TEXT --> PROFANITY_OUT{"Profanity?"}
        UPLOAD_MEDIA --> PROFANITY_OUT
        PREPARE_TEMPL --> SKIP_PROFANE

        PROFANITY_OUT -->|Flagged| REJECT["400 Bad Request<br/>'Message contains inappropriate content'"]
        PROFANITY_OUT -->|Clean| CHECK_WINDOW

        SKIP_PROFANE --> CHECK_WINDOW{"WhatsApp 24h<br/>Window Active?<br/>(if WhatsApp channel)"}

        CHECK_WINDOW -->|Expired| ERR_WINDOW["Error:<br/>'24-hour messaging window expired'"]
        CHECK_WINDOW -->|Active or Non-WA| SEND_PLATFORM

        SEND_PLATFORM["Send to Platform API"]
        SEND_PLATFORM --> WA_API["WhatsApp API<br/>POST /messages"]
        SEND_PLATFORM --> IG_API["Instagram API<br/>POST /messages"]
        SEND_PLATFORM --> TG_API["Telegram API<br/>sendMessage"]

        WA_API --> SAVE_OUT["INSERT conversation_messages<br/>from=agent, to=customer<br/>status: SENT"]
        IG_API --> SAVE_OUT
        TG_API --> SAVE_OUT

        SAVE_OUT --> UPDATE_CONV_OUT["UPDATE conversations<br/>last_message, last_message_at"]
        UPDATE_CONV_OUT --> NOTIFY_OUT["notifyHybrid()<br/>private-room:{convId}<br/>Event: new-message"]
    end

    subgraph DELIVERY["DELIVERY STATUS WEBHOOK"]
        DELIVERY_WB["Platform Delivery Webhook"] --> FIND_MSG{"Find Message<br/>by platform_msg_id"}
        FIND_MSG --> UPDATE_STATUS["UPDATE conversation_messages<br/>status: DELIVERED / READ / FAILED"]
        UPDATE_STATUS --> NOTIFY_DELIVERY["notifyHybrid()<br/>Event: message-updated"]
    end

    style INBOUND fill:#E8F5E9,stroke:#4CAF50
    style OUTBOUND fill:#FFF3E0,stroke:#FF9800
    style DELIVERY fill:#E3F2FD,stroke:#1976D2
```

---

## 4.4 BE Conversation Status Flow

Alur perubahan status conversation.

```mermaid
stateDiagram-v2
    [*] --> UNASSIGNED: Customer sends first message
    UNASSIGNED --> ASSIGNED: Agent claims chat<br/>(POST /get-new-chat)
    UNASSIGNED --> ASSIGNED: Admin assigns agent<br/>(PATCH /status: assignedTo)

    ASSIGNED --> UNASSIGNED: Agent unassigns<br/>(PATCH /status: assignedTo=null)
    ASSIGNED --> RESOLVED: Agent resolves<br/>(PATCH /status + resolveCategoryIds)
    ASSIGNED --> ASSIGNED: Agent sends message<br/>(message flow continues)

    RESOLVED --> ASSIGNED: Customer replies<br/>(new message in chain)
    RESOLVED --> CLOSED: Admin closes<br/>(PATCH /status: CLOSED)

    CLOSED --> ASSIGNED: Customer replies<br/>(new conversation created,<br/>previous_conversation_id linked)

    UNASSIGNED: Unread count tracked
    ASSIGNED: first_response_at set
    ASSIGNED: first_response_by set
    RESOLVED: resolved_at set
    RESOLVED: resolved_by set
    RESOLVED: resolve categories linked
    CLOSED: conversation ended
```

---

## 4.5 BE Campaign Execution Flow (Detailed)

Alur lengkap eksekusi campaign di Backend.

```mermaid
flowchart TD
    TRIGGER["POST /campaigns/:id/execute"] --> LOAD["Load Campaign<br/>+ Template + Channel + Recipient List"]

    LOAD --> STATUS_CHECK{Campaign Status?}
    STATUS_CHECK -->|"DRAFT"| SET_RUNNING["UPDATE campaigns<br/>status: RUNNING<br/>started_at: NOW()"]
    STATUS_CHECK -->|"SCHEDULED"| CHECK_SCHEDULE{Scheduled time<br/>reached?}
    STATUS_CHECK -->|"RUNNING"| ALREADY["Return: Already running"]
    STATUS_CHECK -->|"COMPLETED/CANCELLED"| ERR["Return Error"]

    CHECK_SCHEDULE -->|"Not yet"| SCHEDULE_WAIT["Wait for cron/scheduler"]
    CHECK_SCHEDULE -->|"Yes"| SET_RUNNING

    SET_RUNNING --> LOAD_TEMPLATE["Load Template<br/>templates table<br/>template_components (JSON)"]
    LOAD_TEMPLATE --> LOAD_LIST["Load Recipient List Items<br/>campaign_recipient_list_items<br/>+ contacts"]

    LOAD_LIST --> INIT_COUNTS["Reset campaign counts:<br/>total_recipients, sent, delivered,<br/>read, failed = 0"]

    INIT_COUNTS --> LOOP_START["Start Loop"]

    subgraph LOOP["PER RECIPIENT LOOP"]
        LOOP_START --> GET_NEXT{Next<br/>Recipient?}
        GET_NEXT -->|No| COMPLETE
        GET_NEXT -->|Yes| RENDER["Render Template<br/>Replace {{1}}, {{2}}...<br/>with recipient variables"]

        RENDER --> FIND_CONV{Has Active<br/>Conversation?<br/>contact_id + channel_id}
        FIND_CONV -->|No| CREATE_CONV["INSERT conversations<br/>status: UNASSIGNED"]
        FIND_CONV -->|Yes| USE_CONV["Use existing conversation"]

        CREATE_CONV --> SEND_WA
        USE_CONV --> SEND_WA

        SEND_WA["Send via WhatsApp API<br/>POST /{phone_id}/messages<br/>template type message"]
        SEND_WA --> WA_RESPONSE{WhatsApp<br/>API Response?}

        WA_RESPONSE -->|Success| RECORD_SENT["INSERT campaign_recipients<br/>status: SENT<br/>conversation_id, message_id"]
        WA_RESPONSE -->|Rate Limit| WAIT_RETRY["Wait + Retry<br/>(Exponential backoff)"]
        WA_RESPONSE -->|Error| RECORD_FAIL["INSERT campaign_recipients<br/>status: FAILED"]

        RECORD_SENT --> INC_SENT["UPDATE campaigns<br/>sent++"]
        RECORD_FAIL --> INC_FAIL["UPDATE campaigns<br/>failed++"]

        INC_SENT --> NEXT["Continue Loop"]
        INC_FAIL --> NEXT
        WAIT_RETRY --> SEND_WA

        NEXT --> GET_NEXT
    end

    COMPLETE["All Recipients Processed"] --> SET_COMPLETE["UPDATE campaigns<br/>status: COMPLETED<br/>completed_at: NOW()"]

    subgraph DELIVERY["DELIVERY WEBHOOKS"]
        WA_DELIVERY["WhatsApp Delivery Callback"] --> FIND_RECIP{Find campaign_recipient<br/>by conversation_message_id}
        FIND_RECIP --> UPDATE_RECIP["UPDATE campaign_recipients<br/>status: DELIVERED / READ / FAILED"]
        UPDATE_RECIP --> UPDATE_CAMP_COUNTS["UPDATE campaigns<br/>delivered++ / read++ / failed++"]
        UPDATE_CAMP_COUNTS --> NOTIFY_STATS["Optional: Push notification<br/>to campaign dashboard"]
    end

    style LOOP fill:#E8F5E9,stroke:#4CAF50
    style DELIVERY fill:#FFF3E0,stroke:#FF9800
```

---

## 4.6 BE Ticket Lifecycle Flow (Detailed)

Alur lengkap lifecycle ticket di Backend.

```mermaid
flowchart TD
    CREATE["POST /tickets<br/>Create Ticket"] --> VALIDATE_T["Validate with Zod<br/>ticket.validation.ts"]
    VALIDATE_T --> INSERT_TICKET["INSERT tickets<br/>ticket_number: auto-generated<br/>status: TODO"]

    INSERT_TICKET --> INSERT_ATTACH{"Has<br/>Attachments?"}
    INSERT_ATTACH -->|Yes| INSERT_ATT["INSERT ticket_attachments<br/>(link to ticket_id)"]
    INSERT_ATTACH -->|No| DONE_CREATE
    INSERT_ATT --> DONE_CREATE["Ticket Created<br/>201 Created Response"]

    subgraph LIFECYCLE["TICKET LIFECYCLE"]
        DONE_CREATE --> TODO_ST["Status: TODO"]

        TODO_ST -->|"Assign to Agent<br/>PUT /tickets/:id<br/>assignedTo, assignedBy"| IN_PROGRESS["Status: IN_PROGRESS"]
        TODO_ST -->|"Soft Delete<br/>DELETE /tickets/:id"| DELETED["Status: DELETED<br/>(deleted_at IS NOT NULL)"]

        IN_PROGRESS -->|"Reassign<br/>assignedTo changed"| IN_PROGRESS
        IN_PROGRESS -->|"Resolve<br/>status: RESOLVED<br/>resolvedBy, resolvedAt<br/>resolvedNote"| RESOLVED["Status: RESOLVED"]
        IN_PROGRESS -->|"Reject<br/>status: REJECTED<br/>rejectedBy, rejectedAt<br/>rejectReason"| REJECTED["Status: REJECTED"]

        RESOLVED -->|"Reopen<br/>status back to<br/>IN_PROGRESS"| IN_PROGRESS
        RESOLVED -->|"Mark Done<br/>status: DONE<br/>closedBy, closedAt"| DONE["Status: DONE"]

        REJECTED --> END_STATE["Terminal State"]
        DONE --> END_STATE
        DELETED --> END_STATE
    end

    subgraph HISTORY["STATUS HISTORY TRACKING"]
        STATUS_CHANGE["Any Status Change"] --> RECORD_HIST["INSERT ticket_status_history<br/>ticket_id, old_status, new_status<br/>updated_by, assigned_to<br/>resolved_by, rejected_by"]
        RECORD_HIST --> NOTIFY_TICKET["notifyHybrid()<br/>(if real-time needed)"]
    end

    LIFECYCLE --> HISTORY

    subgraph BATCH["BATCH OPERATIONS"]
        BATCH_ASSIGN["Batch Assign<br/>(FE loops PUT calls)"] --> LOOP_PUT1["PUT /tickets/:id<br/>assignedTo: userId<br/>× N tickets"]
        BATCH_STATUS["Batch Update Status<br/>(FE loops PUT calls)"] --> LOOP_PUT2["PUT /tickets/:id<br/>status: IN_PROGRESS<br/>× N tickets"]
    end

    subgraph FROM_CHAT["TICKET FROM CONVERSATION"]
        CHAT_LINK["conversation_id provided"] --> VERIFY_CONV{"Conversation<br/>Exists?"}
        VERIFY_CONV -->|Yes| LINK_CONV["Set ticket.conversation_id"]
        VERIFY_CONV -->|No| ERR_CONV["Error: Conversation not found"]
        CONTACT_LINK["contact_id provided"] --> VERIFY_CONTACT{"Contact<br/>Exists?"}
        VERIFY_CONTACT -->|Yes| LINK_CONTACT["Set ticket.contact_id"]
        VERIFY_CONTACT -->|No| ERR_CONTACT["Error: Contact not found"]
    end

    style LIFECYCLE fill:#E8F5E9,stroke:#4CAF50
    style HISTORY fill:#FFF3E0,stroke:#FF9800
    style BATCH fill:#E3F2FD,stroke:#1976D2
    style FROM_CHAT fill:#F3E5F5,stroke:#9C27B0
```

---

## 4.7 BE Webhook Processing Architecture

Arsitektur pemrosesan webhook dari external channels.

```mermaid
flowchart TD
    subgraph INCOMING["WEBHOOK RECEIVERS"]
        WH_WA_GET["GET /api/webhook/whatsapp<br/>Verification<br/>(hub.mode, hub.verify_token,<br/>hub.challenge)"]
        WH_WA_POST["POST /api/webhook/whatsapp<br/>Receive messages<br/>(WhatsApp payload)"]
        WH_IG["POST /api/webhook/instagram<br/>Receive DMs + Comments<br/>(Instagram payload)"]
        WH_TG["POST /api/webhook/telegram<br/>Receive messages<br/>(Telegram payload)"]
    end

    subgraph WHATSAPP_FLOW["WHATSAPP FLOW"]
        WH_WA_GET --> VERIFY{Verify Token<br/>Matches?}
        VERIFY -->|Yes| RETURN_CHALLENGE["Return hub.challenge<br/>200 OK"]
        VERIFY -->|No| ERR_403["403 Forbidden"]

        WH_WA_POST --> VERIFY_WA_HUB{"Hub Mode = 'subscribe'?"}
        
        %% Perbaikan Baris 15 & 16: Dipisah ke baris baru
        VERIFY_WA_HUB -->|Yes| RETURN_200["200 OK No processing"]
        VERIFY_WA_HUB -->|No| PARSE_WA["Parse WhatsApp Payload<br/>Extract: entry.changes<br/>messages, statuses"]

        PARSE_WA --> MSG_TYPE{Message Type?}
        MSG_TYPE -->|"messages"| TO_QUEUE["Publish to RabbitMQ<br/>Queue: omnichannel_work_queue<br/>Route: whatsapp"]
        MSG_TYPE -->|"statuses"| PROCESS_STATUS["Process Delivery Status<br/>UPDATE conversation_messages<br/>status: SENT → DELIVERED → READ"]

        TO_QUEUE --> RABBIT_MQ["RabbitMQ<br/>amqplib connection"]
    end

    subgraph RABBIT_WORKER["RABBITMQ WORKER"]
        RABBIT_MQ --> CONSUME["Consume from Queue"]
        CONSUME --> ACK{"Process<br/>Success?"}
        ACK -->|Yes| ACK_MSG["ack message"]
        ACK -->|No| NACK["nack + requeue<br/>(or dead-letter)"]
        NACK --> CONSUME
    end

    subgraph DIRECT_PROCESSING["DIRECT PROCESSING<br/>(Instagram, Telegram)"]
        WH_IG --> PARSE_IG["Parse Instagram Payload"]
        PARSE_IG --> IG_TYPE{Event Type?}
        
        %% Perbaikan: Menghapus karakter asteris (*) yang bisa mengganggu parser
        IG_TYPE -->|messages| INSTA_DM["Process Instagram DM<br/>→ conversation engine"]
        IG_TYPE -->|comments| INSTA_COMMENT["Process Comment<br/>→ conversation engine<br/>context: POST_COMMENT"]

        WH_TG --> PARSE_TG["Parse Telegram Payload"]
        PARSE_TG --> TG_PROCESS["Process Telegram Message<br/>→ conversation engine"]
    end

    subgraph SHARED_PROCESSOR["SHARED MESSAGE PROCESSOR"]
        TO_QUEUE --> PROCESSOR["Message Processor Service"]
        INSTA_DM --> PROCESSOR
        INSTA_COMMENT --> PROCESSOR
        TG_PROCESS --> PROCESSOR
        
        PROCESSOR --> CONTACT_RESOLVE["Resolve/Create Contact"]
        PROCESSOR --> CONV_RESOLVE["Resolve/Create Conversation"]
        PROCESSOR --> SAVE_MESSAGE["Save Message + Attachments"]
        PROCESSOR --> CHECK_AUTO_RESP["Check Auto Response"]
        PROCESSOR --> NOTIFY_RT["Real-time Notification"]
    end

    PROCESS_STATUS --> DB_UPDATE["Direct DB Update"]
    DB_UPDATE --> DB[(PostgreSQL)]
    SHARED_PROCESSOR --> DB

    style INCOMING fill:#E8F5E9,stroke:#4CAF50
    style WHATSAPP_FLOW fill:#FFF3E0,stroke:#FF9800
    style RABBIT_WORKER fill:#FFEBEE,stroke:#F44336
    style DIRECT_PROCESSING fill:#E3F2FD,stroke:#1976D2
    style SHARED_PROCESSOR fill:#F3E5F5,stroke:#9C27B0
```

---

## 4.8 BE Service Module Pattern

Pattern yang digunakan di setiap service module Backend.

```mermaid
flowchart TD
    subgraph MODULE["Service Module Structure"]
        direction TB
        ROUTE["*.route.ts<br/>───────────────<br/>Define Hono routes<br/>Apply middleware<br/>Call service functions<br/>Return responses"]
        SVC["*.service.ts<br/>───────────────<br/>Business logic<br/>Orchestrate repos<br/>Handle errors<br/>Trigger notifications"]
        REPO["*.repository.ts<br/>───────────────<br/>Drizzle ORM queries<br/>INSERT / SELECT<br/>UPDATE / DELETE<br/>Transactions"]
        VALID["*.validation.ts<br/>───────────────<br/>Zod schemas<br/>Input validation<br/>Output parsing"]
        ERR["*.error.ts<br/>───────────────<br/>Custom error classes<br/>Error codes<br/>Error messages"]
        TYPE["*.type.ts<br/>───────────────<br/>TypeScript types<br/>Interfaces<br/>Enums"]
    end

    ROUTE -->|"Validate input"| VALID
    ROUTE -->|"Call business logic"| SVC
    SVC -->|"Query database"| REPO
    SVC -->|"Throw errors"| ERR
    REPO -->|"Read types"| TYPE
    ROUTE -->|"Read types"| TYPE
    SVC -->|"Read types"| TYPE

    subgraph EXAMPLE["Example: conversation-message"]
        EX_ROUTE["conversation-message.route.ts<br/>GET / → listMessages()<br/>POST / → sendMessage()<br/>POST /template → sendTemplate()"]
        EX_SVC["conversation-message.service.ts<br/>sendMessage():<br/>  1. Validate profanity<br/>  2. Check 24h window<br/>  3. Send to platform API<br/>  4. Save to DB<br/>  5. Notify real-time"]
        EX_REPO["conversation-message.repository.ts<br/>findById()<br/>findByConversationId()<br/>create()<br/>updateStatus()"]
        EX_VALID["conversation-message.validation.ts<br/>sendMessageSchema<br/>sendTemplateSchema<br/>listMessagesSchema"]
    end

    EX_ROUTE --> EX_SVC
    EX_SVC --> EX_REPO
    EX_ROUTE --> EX_VALID

    style MODULE fill:#F5F5F5,stroke:#9E9E9E
    style EXAMPLE fill:#E3F2FD,stroke:#1976D2
```

---

## 4.9 BE Error Handling Flow

```mermaid
flowchart TD
    REQUEST["Request Handler"] --> TRY["try { ... }"]
    TRY --> HANDLER["Business Logic"]
    HANDLER --> SUCCESS{Success?}

    SUCCESS -->|Yes| RETURN_OK["response.util.success(data)<br/>200 OK"]

    SUCCESS -->|No| ERROR_CATCH["catch (error)"]
    ERROR_CATCH --> ERROR_TYPE{Error Type?}

    ERROR_TYPE -->|"AppError"| APP_ERR["Custom App Error<br/>status + message + code"]
    ERROR_TYPE -->|"HTTPException"| HTTP_ERR["Hono HTTP Exception"]
    ERROR_TYPE -->|"DrizzleQueryError"| DB_ERR["Database Query Error"]
    ERROR_TYPE -->|"PostgresError"| PG_ERR["PostgreSQL Error<br/>(unique violation, fk, etc.)"]
    ERROR_TYPE -->|"ZodError"| ZOD_ERR["Validation Error<br/>Input format invalid"]
    ERROR_TYPE -->|"Unknown"| UNKNOWN_ERR["Unknown Error"]

    APP_ERR --> MAP_STATUS["Map to HTTP Status"]
    HTTP_ERR --> MAP_STATUS
    DB_ERR --> LOG_ERR["Log Error<br/>console.error"]
    PG_ERR --> LOG_ERR
    ZOD_ERR --> MAP_400["400 Bad Request<br/>Validation error details"]
    UNKNOWN_ERR --> LOG_ERR

    LOG_ERR --> MAP_500["500 Internal Server Error"]
    MAP_STATUS --> RESPONSE["Return Error Response<br/>response.util.error()"]
    MAP_400 --> RESPONSE
    MAP_500 --> RESPONSE

    style REQUEST fill:#C8E6C9,stroke:#4CAF50
    style ERROR_CATCH fill:#FFCDD2,stroke:#F44336
    style RESPONSE fill:#BBDEFB,stroke:#2196F3
```
