Skip to content

🏷️ Domain 领域模型概览

pkg/domain 是 Composer Skills 的数据结构层。它不包含任何业务逻辑,只定义与 Packagist REST API 以及 Composer 元数据对应的 Go 结构体,供 pkg/client(API 客户端)和 pkg/repository(仓库访问层)在请求/响应反序列化时使用。

一句话定位

domain 包是「网络字节 → Go 结构体」的契约层:API 返回什么 JSON,这里就有对应什么 struct。

📦 包路径

  • 导入路径github.com/scagogogo/composer-skills/pkg/domain
  • 包名domain
  • 依赖:仅依赖 Go 标准库(time),零外部依赖,可被任何模块安全引用

🧩 为什么需要独立的 domain 层

关注点说明
🔄 解耦API 客户端(pkg/client)只负责 HTTP,不感知字段含义;业务代码只消费 struct,不感知 JSON
🧪 可测试测试时可直接构造 domain 结构体做断言,无需 mock 网络
📊 可持久化多数字段同时标注了 jsonbson tag,可直接落库 MongoDB
🎯 类型安全把松散的 JSON 映射成强类型 Go 字段,编译期捕获字段拼写错误

📋 类型清单

按用途分为六大类,每类对应一个子文档:

📦 包信息模型 — package.md

类型用途来源文件
PackageInfo包的基本信息(名称、描述、GitHub 统计、下载量等)package.go
ComposerPackageInfo包的完整信息(含小写包名、md5、三个时间戳)package.go
PackageDownloads下载统计(总/月/日)package.go
Maintainer维护者(姓名、头像)maintainer.go
Version单个版本的完整元数据(依赖、源码、dist、license 等)version.go

🔒 安全公告模型 — advisory.md

类型用途来源文件
AdvisoriesResponse安全公告顶层响应(按包名分组)advisory.go
Advisory单条安全公告(CVE、受影响版本、来源)advisory.go
Source公告来源(GitHub、NVD 等)advisory.go

🔍 搜索结果模型 — search.md

类型用途来源文件
SearchResponse搜索响应顶层(结果列表 + 分页)search.go
SearchResult单条搜索结果search.go

📊 统计模型 — statistics.md

类型用途来源文件
StatisticsResponse仓库统计顶层statistics.go
Totals仓库总量(下载/包/版本)statistics.go
PackageStatsResponse单包下载统计package_stats.go
ChangeTrackingResponse元数据变更跟踪响应package_stats.go
ChangeAction单条变更操作(update/delete)package_stats.go

🛠️ 创建/编辑/列表模型 — create-package.md

类型用途来源文件
PackageCreateRequest / PackageCreateResponse创建包create_package.go
PackageEditRequest / PackageEditResponse编辑包create_package.go
PackageUpdateRequest / PackageUpdateResponse更新包create_package.go
PackageListResponse包名列表package_list.go
PackageListWithDataResponse带附加数据的包列表package_list.go
PackageData包附加数据(类型/仓库/废弃状态)package_list.go
PopularPackagesResponse / PopularPackage流行包列表popular_packages.go

🔗 类型与 API 方法的对应关系

下表列出 pkg/client.ComposerClient 的方法与返回/入参 domain 类型的对应:

ComposerClient 方法入参 domain 类型返回 domain 类型
GetPackage*ComposerPackageInfo
GetStatistics*StatisticsResponse
GetSecurityAdvisories*AdvisoriesResponse
GetSecurityAdvisoriesForPackages*AdvisoriesResponse
GetSecurityAdvisoriesSince*AdvisoriesResponse
ListPackages / ListPackagesByVendor / ListPackagesByType*PackageListResponse
ListPackagesWithData*PackageListWithDataResponse
ListPopularPackages*PopularPackagesResponse
SearchPackages / SearchPackagesByTags / SearchPackagesByType*SearchResponse
GetPackageStats*PackageStatsResponse
GetPackageChanges*ChangeTrackingResponse
CreatePackage*PackageCreateRequest*PackageCreateResponse
EditPackage*PackageEditRequest*PackageEditResponse
UpdatePackage*PackageUpdateRequest*PackageUpdateResponse

同样适用于 Repository 层

pkg/repository.Repository 的方法(如 StatisticsListSecurityAdvisoriesListAdvisories)也复用同一套 domain 类型,保证两层行为一致。

🚀 快速示例

从 Packagist 拿到一个包的信息后,domain 结构体长这样:

go
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/scagogogo/composer-skills/pkg/client"
    "github.com/scagogogo/composer-skills/pkg/domain"
)

func main() {
    c := client.NewComposerClient(30*time.Second)
    info, err := c.GetPackage("symfony/console")
    if err != nil {
        log.Fatalf("获取包信息失败: %v", err)
    }

    // info 是 *domain.ComposerPackageInfo
    fmt.Printf("包名: %s\n", info.PackageName)
    fmt.Printf("描述: %s\n", info.Package.Description)
    fmt.Printf("总下载量: %d\n", info.Package.Downloads.Total)
    fmt.Printf("GitHub Stars: %d\n", info.Package.GithubStars)
    for name := range info.Package.Versions {
        fmt.Printf("版本: %s\n", name)
    }
}

📚 下一步

基于 MIT 许可证发布