Skip to content

deccara-tech/std-bot

Repository files navigation

Bot Platform Template (Transport-Agnostic)

This repository is a production-minded Go boilerplate for a multi-channel bot platform using Hexagonal architecture.

It is designed to be used as a GitHub organization template, so defaults and structure prioritize reuse across teams.

It is not a WhatsApp-specific bot codebase. WhatsApp (aldinokemal/go-whatsapp-web-multidevice) is integrated as one replaceable transport gateway adapter.

Why GOWA Is Adapter-Only

internal/adapters/secondary/messaging/whatsapp/gowa is an anti-corruption boundary:

  • GOWA DTOs stay local in gowa/dto
  • mapping layer converts provider payloads to canonical internal/transport contracts
  • app/core logic never imports provider DTOs

This keeps transport integrations replaceable and allows later Discord/Slack adapters.

Architecture Overview

  • internal/core/domain: business entities/value objects
  • internal/core/ports: foundational interfaces consumed by app/core
  • internal/transport: canonical inbound/outbound contracts
  • internal/app/usecase: orchestration and execution logic
  • internal/app/workflow: stateful flow skeletons
  • internal/app/policy: auth/rate-limit/feature flags
  • internal/adapters/primary/http: webhook/API/health HTTP edges
  • internal/adapters/secondary: messaging, queue, persistence, AI implementations
  • internal/platform: config/bootstrap/db/cache/observability wiring

Dependency direction:

  • core <- app <- adapters/platform/wiring
  • provider-specific DTOs and HTTP payloads remain at adapter edge

Architecture Mindset

This project is built as a modular monolith with strict ports-and-adapters boundaries:

  • domain and ports define stable internal contracts
  • use cases orchestrate behavior without provider coupling
  • adapters translate external systems to canonical contracts
  • platform/bootstrap wires dependencies without leaking infrastructure into business logic
flowchart LR
    EXT[External Systems<br/>WhatsApp/GOWA, DB, Redis] --> ADP[Adapters]
    ADP --> TRANS[Canonical Transport Contracts]
    TRANS --> APP[App Use Cases + Policies + Workflows]
    APP --> CORE[Core Domain + Ports]
    CORE --> APP
    APP --> ADP
    ADP --> EXT
Loading

How To Code (Structure Walkthrough)

When adding a feature, move top-down by responsibility, not by provider:

  1. start from canonical contract or domain model
  2. add or update use case orchestration
  3. implement adapter translation and IO
  4. wire in bootstrap
  5. test mapper + use case + integration path
flowchart TD
    A[Feature Request] --> B{Transport-facing?}
    B -- Yes --> C[internal/transport]
    B -- No --> D[internal/core/domain]
    C --> E[internal/core/ports if needed]
    D --> E
    E --> F[internal/app/usecase]
    F --> G[internal/adapters/secondary]
    F --> H[internal/adapters/primary/http]
    G --> I[internal/platform/bootstrap wiring]
    H --> I
    I --> J[test/contract + test/integration + usecase tests]
Loading

Big Picture (Small Repo vs Big System)

In isolation, this repo is one reusable bot execution unit. In a larger system, it acts as a channel-agnostic automation service behind API gateways, event buses, and shared data platforms.

flowchart LR
    subgraph EnterpriseSystem["Big System"]
      API[API Gateway / Internal Services]
      BUS[Event Bus]
      OBS[Central Observability]
      DATA[Data Platform / BI]
    end

    subgraph BotTemplateRepo["This Project (Small Unit)"]
      SERVER[bot-server]
      WORKER[worker]
      APPMOD[App Use Cases + Workflows]
      ADAPTERS[Messaging/Queue/Persistence Adapters]
    end

    API --> SERVER
    SERVER --> BUS
    BUS --> WORKER
    WORKER --> APPMOD
    APPMOD --> ADAPTERS
    ADAPTERS --> DATA
    SERVER --> OBS
    WORKER --> OBS
Loading

Execution Model

Inbound flow:

  1. HTTP webhook receives provider payload
  2. webhook handler transforms payload to canonical inbound event
  3. event enqueued to async queue

Worker flow:

  1. worker consumes queued canonical event
  2. app use case orchestrates policy + message handling
  3. optional AI hook can be used
  4. messenger port sends outbound message

Implemented Vertical Slice: Ping -> Pong

  1. GOWA webhook payload mapped to canonical event
  2. canonical event enqueued in queue adapter (asynq package)
  3. worker ingests event via ingest_event
  4. message path parses command text
  5. if inbound text is ping, worker replies pong through core/ports.Messenger
  6. GOWA sender adapter sends outbound payload

Implemented Vertical Slice: Invoice PDF (inv)

  1. User sends command prefix: inv customer=<name> item=<name> qty=<int> price=<number> [currency=<code>]
  2. Worker parses command and runs generate_invoice_document use case
  3. Use case builds a PDF invoice and stores it through InvoiceRepository
  4. Messenger sends the generated PDF back through GOWA /send/file endpoint

Folder Responsibilities

  • cmd/bot-server: thin HTTP ingress process
  • cmd/worker: async business execution process
  • internal/transport/event: canonical inbound event model
  • internal/transport/message: canonical outbound model + capability model
  • test/contract: interface contract tests
  • test/integration: integration placeholders and future E2E tests

Coding Conventions

  • explicit constructors (New...)
  • interfaces are explicit and minimal
  • no junk-drawer utils/helpers/services packages
  • app/core never depend on provider DTOs
  • keep webhook layer thin and async-first

Architecture Boundary Enforcement

Compile-time import tests prevent layer violations:

go test ./internal/... -run "Test(Core|Transport|App|Adapters)DoesNot"

Boundary rules:

  • core: no imports from adapters, app, transport, or platform
  • transport: no imports from adapters, app, or platform
  • app: no imports from adapters or platform
  • adapters: may import core and transport, but not app or platform

Violations fail at go test, not at code review.

Template Governance

  • preserve architecture boundaries; avoid structural churn
  • keep defaults generic, configurable, and provider-replaceable
  • avoid machine-local or organization-private paths in committed docs/scripts
  • treat new capabilities as modular additions, not repo-shape rewrites

Local Development

Requirements:

  • Go 1.22+
  • Docker + Docker Compose

Commands:

  • make tidy
  • make fmt
  • make test
  • make up
  • make down

Run server and worker locally without dockerized Go runtime:

  • make run-server
  • make run-worker

Docker Compose

docker-compose.yml includes:

  • bot-server
  • worker
  • gowa
  • redis
  • postgres

Environment knobs:

  • APP_DB_BACKEND=postgres|sqlite
  • APP_POSTGRES_DSN=... (postgres mode)
  • APP_SQLITE_PATH=./data/bot.db (sqlite mode)
  • APP_GOWA_SEND_MESSAGE_PATH=/send/message
  • APP_GOWA_SEND_FILE_PATH=/send/file

Extension Guide (Discord/Slack)

  1. Add adapter under internal/adapters/secondary/messaging/<provider>
  2. keep provider DTOs local to adapter package
  3. implement core/ports.Messenger
  4. map provider inbound payload to canonical transport/event
  5. map canonical outbound message to provider wire DTO
  6. register provider-specific webhook route at HTTP edge

Rules To Preserve

  • keep provider DTOs at adapter edge
  • keep canonical contracts in internal/transport
  • keep business flow in worker by default
  • avoid direct app/core imports of provider-specific packages

Roadmap

  • real Postgres persistence implementations
  • delivery status reconciliation worker path
  • richer workflow engine and session lifecycle
  • observability metrics/tracing and dashboards
  • multi-tenant authorization and feature flags
  • Discord/Slack transport adapters

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors