Skip to content

Модуль бичих

Бизнес апп нь энэ репод байдаггүй. Өөрийн репод амьдарч, pkg/nexus-ээр өөрийгөө бүртгүүлж, каталогоор ирдэг. Энэ баримт нь backend/pkg/nexus-ийн кодоос уншиж бичигдсэн.

Баримтын төв · Архитектур


Хамгийн бага модуль

main.go дотор гурван мөр:

func main() {
    mymodule.New(db)   // өөрөө nexus.Register дуудна
    host.Run()
}

nexus.Register нь компиляцын үеийн бүртгэл. Платформ маршрут mount хийх, цэс барих, суулгасан апп ард нь код байгаа эсэхийг шалгахдаа энэ бүртгэлээс асууна. Багцын түвшний төлөв байдаг нь зориуд: «энэ програм ямар модуль агуулж байна» гэдэг нь програмын шинж чанар бөгөөд бүртгэлийг конструктор болгоноор дамжуулбал зөвхөн хоёр нь зөрөх боломж л нэмэгдэнэ.

Нэг id-г хоёр модуль нэхэх нь угсралтын алдаа, ажиллагааны нөхцөл биш — хоёулаа нэрээрээ компиляцлагдсан байдаг — тиймээс сүүлийнх нь ялна.

Модуль юу зарлах вэ

type Module interface {
    ID() string
    Name() string
    Version() string
    Dependencies() []Dependency
    Permissions() []PermissionDefinition
    Menus() []MenuDefinition
    RegisterRoutes(r chi.Router, workspaceAuth func(http.Handler) http.Handler)
}

RegisterRoutes нь урьдчилан хүрээлэгдсэн бүлэг биш, үндсэн router-ыг авна. Хамт ирэх middleware нь платформын аль хэдийн гаргасан хоёр шийдвэрийг авч явна: энэ байгууллага аппыг суулгасан эсэх, ба — платформ эрхийн угтварыг нь мэддэг апп бол — дуудагч ийм төрлийн хүсэлт хийж болох эсэх.

Эрхийг платформд даалгах

type AccessPolicy interface {
    MenuPermission() string
    RoutePermissionPrefix() string
}

Хэрэгжүүлэх нь заавал биш. Хэрэгжүүлээгүй модуль зөвхөн «суулгасан эсэх»-ээр хамгаалагдана, эрхээ өөрөө шалгах үүрэгтэй.

Хоосон мөр нь бодит хариулт, орхигдсон зүйл биш: «платформ үүнийг бүү хаа» гэсэн үг. Үүнийг чангаар хэлэх нь чухал — өмнө нь чимээгүй байх нь «эрх шаардлагагүй» ба «энэ модуль өөрөө шалгадаг» хоёрыг хоёуланг нь илэрхийлдэг байсан бөгөөд кодод тэднийг ялгах арга байгаагүй.

RoutePermissionPrefix нь GET/HEAD-д prefix.read, өөрчилж чадах бүхэнд prefix.manage шаардана. Дүрэм нь үйл үгээр илэрхийлэгдэхээс нарийн бол — жишээ нь өргөдөл гаргагчийн харьяалагдах нэгжээс хамаарах зөвшөөрлийн алхам — хоосон үлдээгээд өөрөө шалгана.

Энэ нь өмнө нь платформ дотор апп бүрийг нэрээр нь жагсаасан хоёр switch байсны оронд бий болсон. Өөр репод амьдардаг модуль энэ репо доторх switch-д өөрийгөө нэмж чадахгүй, тэгээд юу болдог вэ гэвэл compile алдаа биш — чимээгүй сулрал: салгагдсан апп ажилласаар, хажуугийн самбарт харагдсаар, харин хаагдахаа болино. Refactor-ийн үеэр чимээгүй алга болдог эрхийн шалгалт бол энэ төрлийн алдааны хамгийн муу хэлбэр.

Модулийн өөрийн schema

Модуль өөрийн migration-ыг embed хийж, конструктораасаа өгнө:

//go:embed migrations/*.sql
var migrations embed.FS

Яагаад ийм болсныг мэдэх нь чухал: өмнө нь хүснэгт үүсгэх цорын ганц газар нь платформын migration хавтас байсан. Үр дүнд нь суулгац бүр энд хэзээ нэгэн цагт бичигдсэн бүх аппын schema-г авч явдаг байв — худалдаа, POS, төрийн үйлчилгээний урсгал зэрэг энэ репод байхаа больсон аппуудынх ч дотроо.

Модулийн migration нь суулгах үед л ажилладаг. Апп «суулгагдсан» атлаа хүснэгтгүй байх боломжтой — тэр нь суулгалт нь migration-аас өмнө бичигдсэн гэсэн үг.

Платформоос юу авах вэ

type Platform interface {
    DB() DB
    Permissions() PermissionStore
}

DB нь байгууллагад уясан холбоо: dbguard нь холболт бүрийг дуудагчийн байгууллагад уядаг тул модулийн query нь өөр байгууллагын мөр харах боломжгүй (Архитектур).

BeginTx нь pgx.ReadOnly-г дэмждэг: «энэ бичихгүй» гэдгээ компиляторт төдийгүй өгөгдлийн санд хэлж чаддаг байх ёстой.

Илүү өргөн боломжууд

Платформын өгдөг бусад зүйлс — бүгд pkg/nexus-д:

nexus.Audit Audit мөр бичих. Модуль internal/workspace/audit-ыг import хийж чадахгүй тул энэ зам байдаг
nexus.RegisterReport Тайлан зарлах; тайлангийн хөдөлгүүр түүнийг ажиллуулна
nexus.RequirePermission Эрхийн middleware
nexus.RateLimit, nexus.QuotaGate Хязгаарлалт
nexus.Documents, nexus.RecordSigned Гарын үсгийн зам
nexus.EID, nexus.DAN Танилтын зам
nexus.People, nexus.Directory Хүн, лавлах
nexus.Provide / nexus.Capability Чадварыг өгөх, авах
nexus.Secrets Нууц утга

TestTheSDKDoesNotDependOnInternal нь SDK нь internal/-ээс хамаарахгүйг, TestTheExportedAPIIsTheOneOnRecord нь экспортын гадаргуу санамсаргүй өөрчлөгдөхгүйг шалгана. Хоёулаа нэг зорилготой: өөр репод бичигдсэн модуль маргааш ч компиляцлагдах ёстой.

Каталог

Апп нь catalog/apps.json-оор ирнэ. Энэ репод тэр файл [] — платформ нэг ч бизнес аппгүйгээр ачаална. Тэр нь дутуу ажил биш, экосистемийн стратегийн тавьсан шалгуур яг тэр байсан.

Каталогийн бичлэг нь платформын хувилбарыг нэрлэдэг ("platform": ">=1.1.0") ба суулгац өөрийн хувилбарыг config.PlatformVersion-оос хэлнэ. Тэр утга нь release build дээр ldflags-аар тавигддаг.

Хувилбарын амлалт

pkg/nexus нь нэг major хувилбарын дотор эвдэрдэггүй.

Build шалгаж чадахгүй амлалт бол хэн нэгэн яарсан үдээс хойш хүртэл сахидаг амлалт. Энэ нь хэн нэгэн эвдэхээр шийдсэнээс болж эвдрэхгүй — refactor хийх явцад параметрийн нэр солигдож, эсвэл distribution-ы төрөл хэрэгжүүлдэг interface-д метод нэмэгдэж эвдэрнэ. Хоёулаа энэ репод нэг ч тестийг унагахгүй. Оронд нь distribution бүрийн build хэдэн өдрийн дараа унана.

Тиймээс экспортын гадаргуу нь golden файл:

go test ./pkg/nexus -update    # санаатай API өөрчлөлтийн дараа

Үүнийг өөрчлөх нь зөвшөөрөгдсөн, ихэвчлэн зөв ч байдаг — энэ бол хөлдөөлт биш. Гэхдээ санамсаргүй болох боломжгүй, мөн review дэх diff нь экосистемийн гэрээ юу авсан, юу алдсаныг яг хэлнэ.

Эвдэх өөрчлөлт гэж юу вэ: экспортолсон interface-д метод нэмэх нь эвдэх өөрчлөлт. Тэр interface-ийг өөр репод хэн нэгэн хэрэгжүүлсэн байдаг.

v2-т орох гэж тэмдэглэгдсэн зүйлс кодод Deprecated: гэж бичигдэнэ. Жишээ нь эрхийн дагаврын дүрэм (.read бүгдэд, .manage менежерүүдэд): нэрлэх конвенц нь гэрээ байх ёсгүй — «үйлчилгээнд өргөдөл гаргах»-ыг илэрхийлж чадахгүй, шалгагдаж чадахгүй, түүнийг сонсоогүй модуль болгоны хувьд хэн юу хийж болохыг чимээгүй шийддэг.