สถาปัตยกรรม Hexagonal และ composition root
backend ของ SML Ai Account เป็น Go module github.com/smlsoft/smlaiaccountapi (go 1.26.0, go.mod:1,3) จัดชั้นแบบ Hexagonal: domain (กฎล้วน) → application (use case + port) → adapter (inbound = HTTP, outbound = DB/ไฟล์/MinIO) โดย internal/di/container.go เป็นจุดเดียวที่ประกอบ (wire) ทุกชั้นเข้าด้วยกัน.
ทำงานอย่างไร
cmd/api/main.goเรียกconfig.Load()→logger.New()→di.Build(cfg, log)→httpserver.New(port, container.Handler, log)(วงจรชีวิตเซอร์วิสและการรัน binary เดียวหลาย OS (Service lifecycle)).di.Build(internal/di/container.go:62) ประกอบตามลำดับ:response.SetLogger(log)(:63) ให้ helper ตอบ error มี logger (รูปแบบ error และ response ของ API (apperr + response helper)).db.NewPoolManager()(:65) pool กลาง (ตัวจัดการ connection pool ต่อฐานข้อมูล (PoolManager)).- tenant:
NewJSONFileRepository(cfg.ConfigDir)+NewBase64Codec()+NewConnectionTester()→apptenant.NewService(:67-74) (ทะเบียน Provider แบบไฟล์ JSON (Provider registry)). - identity: resolver +
UserRepository+CompanyRepository+ revocation store +JWTTokenService→appidentity.NewService(:76-92) (ขั้นตอน login / เลือกบริษัท / refresh / logout (Identity login flow), JWT session token และการเพิกถอน (JWT + revocation store)). - query:
dynamicsql.NewGateway(pools)→appquery.NewService(:94-97) (Query Gateway: /query/list, limit, batch, exec, execBatch). - process:
appprocess.NewService(queryConnectionResolver, pools)(:99-100) — engine นี้อยู่นอกขอบเขตโน้ตชุดนี้ ดู ภาพรวมกลุ่ม process ของ Backend (Process overview). - blob:
blobsql.NewGateway(pools)→appblob.NewService(:102-105) (แทรกแถวพร้อมรูปภาพ: POST /blob/insert + MinIO (Blob insert)). - handler ของโมดูล (admin, dashboard, crm, ai, wms, tms, procure, etax, market, pos, autocare, monitormap, mcp) (
:107-119) (Router, middleware และแผนผัง route ทั้งหมดของ backend (chi router)). inboundhttp.NewRouter(...)พร้อมExtraHandlers{... Rights: identityService}(:147-178);Rightsคือตัวโหลดสิทธิ์เมนูให้RequireRight(สิทธิ์เมนูและ middleware RequireRight (Menu rights / permission)).
Container{Config, Logger, Pools, Handler}(:54-59);Close()เรียกPools.CloseAll()(:184-186) ตอนปิดเซอร์วิส.
กติการะหว่างชั้น (ตามคำอธิบายใน README)
domain: Go ล้วน ไม่ import SQL/HTTP/framework.application: importdomainเท่านั้น เป็นที่อยู่ของ port (port.go) และ use case (service.go).adapter/inbound/*: เรียก inbound port;adapter/outbound/*: implement outbound port ด้วย pgx/ไฟล์/MinIO.platform/*: ของข้ามชั้น (config, logger, db pool, security, apperr) (README.md:47-56).
โครงสร้างจริงใต้ backend/internal (ตรวจจากต้นไม้ไฟล์)
| ชั้น | โฟลเดอร์ที่มีจริง |
|---|---|
domain |
blob, identity, query, tenant |
application |
blob, identity, process, query, tenant |
adapter/inbound/http |
admin, ai, autocare, blob, crm, dashboard, etax, identity, market, mcp, middleware, monitormap, ops, pos, process, procure, query, response, tenant, tms, wms + router.go |
adapter/outbound |
blob, blobsql, crypto, dynamicsql, identity, query, tenant |
platform |
apperr, config, db, docno, httpserver, logger, search, security, sqllit |
รูปแบบซ้ำ: ทุก context ที่ต้องต่อ DB ผู้เช่ามี provider_connection_resolver.go ใน outbound (identity/query/blob) ซึ่งห่อ tenantService.GetProviderConnection เพื่อให้ application ไม่รู้จักไฟล์ config (โมเดลผู้เช่าหลายราย: Tenant, Provider, Main DB, Company (Multi-tenant model)).
จุดที่โค้ดจริงต่างจากแผนใน README
- README (
README.md:62-85) วางแผนโฟลเดอร์adapter/inbound/consumer,outbound/postgres,storage,mqpublisher,pdf,qrcode, และgolang-migrate— ในต้นไม้จริงไม่มีโฟลเดอร์เหล่านี้ และgo.modไม่มี golang-migrate (ไฟล์ migration และการสร้างตารางตอนรัน (Migrations / runtime DDL)). ตาราง bounded context (README.md:90-110) ระบุหลาย context ว่า "ยังไม่เริ่ม" แต่ router มีpos/dashboard/wmsฯลฯ อยู่แล้ว — README ล้าสมัย ให้ยึดโค้ด. - handler ของโมดูล (wms, tms, procure, etax, market, crm, autocare, monitormap) ไม่ผ่าน repository เฉพาะของตัวเอง แต่เรียก use case ของ
query/blobผ่านExecInput.AllowDDL = trueเพื่อสร้างตาราง/เขียนข้อมูลเอง (application/query/port.go:64, เช่นwms/handler.go:1283) และบางตัวมีEnsureSeededที่diผูกต่อกัน (container.go:120-145). - ADR/คำศัพท์โดเมน (Tenant, Main Database, Company, DataGroup, Access Grant, Session, Pre-auth token) นิยามไว้ที่
CONTEXT.md:7-41(โมเดลผู้เช่าหลายราย: Tenant, Provider, Main DB, Company (Multi-tenant model)).
ที่มาในโค้ด
backend/go.mod:1-3— ชื่อ module และเวอร์ชัน Go.backend/internal/di/container.go:62-182— ลำดับการ wire ทั้งหมด;:184Close.backend/README.md:28-56— หลักการ Hexagonal และกติกา dependency.backend/internal/application/query/port.go:64—AllowDDLที่โมดูลใช้.
ดูเพิ่ม
ภาพรวม Backend Go ของ SML Ai Account (Backend overview) · Router, middleware และแผนผัง route ทั้งหมดของ backend (chi router) · ตัวแปรสภาพแวดล้อมและการโหลด config ของ backend (Environment config) · วงจรชีวิตเซอร์วิสและการรัน binary เดียวหลาย OS (Service lifecycle) · สารบัญ ADR ของ backend (Architecture Decision Records)