『Go 语言学习专栏』-- 第十期
![](https://img.haomeiwen.com/i1818135/8e2d164ff8fdeade.png)
![](https://img.haomeiwen.com/i1818135/e58f2d92bd02a165.png)
大家好,我叫谢伟,是一名程序员。
最近在考虑换个环境...
这一系列的文章的宗旨是:学习Go, 不断的编码,得到你想要完成的初级目的,再达到你想实现的中级目的。高级的目的,需要持续不断的重复、学习、更新、迭代...
本节我们实现一个需求:读取用户配置文件,更新至指定目录下。
为什么要实现这样一个需求?
在实际的后台开发过程中,实际上我们很多的交互都是在处理文件,数据量大或者需要处理的数据需要结构化、持久化,才会考虑数据库。
本节我们实现的功能大概是这样:
- 指定目录下创建一个实例(即包含配置文件的全集, 不同用户对应不同的实例),
- 配置文件的全集来自于本地或者克隆于 git 服务器
- 读取用户配置
- 更新至实例内的配置文件内
整体的流程图如下:
![](https://img.haomeiwen.com/i1818135/e3217cd7a6ac82bd.png)
项目组织结构
这个环节,其实很重要的,我现在的方式是不断的更新之前文章提到的 项目组织结构。不断的更新迭代,直到掌握 《领域驱动设计》。
同时有朋友推荐下面这个视频:(全方位讲解如何写出更符合Go 风格的代码)
有兴趣的看下:
├─app
├─config
│ └─user-cfg
├─domain
│ ├─constants
│ ├─objects
│ └─roles
├─infra
│ └─golang
│ ├─execadapter
│ ├─ioutiladapter
│ ├─jsonadapter
│ ├─osadapater
│ └─yamladapter
├─main
├─ui
│ └─service
└─vendor
app
即这个需求的抽象,比如这是个配置中心的内容更新,则:app/config_instance.go
config
cfg.yml
struct_tree.yml
用户配置入口,cfg.yml 用户配置文件,struct_tree.yml 对照表,便于根据对照表获取到用户配置文件 cfg.yml 内的字段和内容。
用户配置文件内容在这里没有任何实际意义,只是为演示使用。
ConfigResume
one
two
three
...
用户示例化之后的文件,把 cfg.yml 内的文件分拆后,写入 ConfigResume 文件夹下的对应的文件内。不同的用户约定实例化的名词不同,比如 one、two、three
domain
constants
roles
create_config_file.go
write_config_file.go
领域信息,constants 内包含常量,比如文件夹和文件的名词等。roles 则表示领域内的具体的动作,比如创建一个配置实例 create_config_file、写入用户信息 write_config_file.go
infra
golang
execadapter
ioutiladapter
jsonadapter
yamladpater
基础设施,包括:执行系统命令 exec、读取和写入文件 ioutil、json 序列化和反序列化、yaml 序列化和反序列化。
main
main.go
主函数入口
ui
service
consist_body.go
git_server.go
read_struct_tree.go
用户表现层,比如组成用户的配置内容、git 服务(clone、add、status、commit) 等、读取对照表
整体的项目的组织调度图大概这样(注意防止循环导入)
![](https://img.haomeiwen.com/i1818135/8e41572cdd47ae19.png)
app 调用 domain 函数或者方法,domain 调用 ui 内service.
infra 作为基础设施层,都可以被domain 和 ui 内 service 调用。
infra 层用法示例
package execadapter
import (
"errors"
"fmt"
"os/exec"
)
// 功能点:任意目录下执行终端命令
var (
ErrCmdNotFound = errors.New("cmd not found")
ErrCmdFail = errors.New("cmd exec fail")
)
func Execute(dir string, cmd string, args ...string) error {
var (
ok string
err error
)
ok, err = exec.LookPath(cmd)
if err != nil {
fmt.Println(ErrCmdNotFound, ok)
return ErrCmdNotFound
}
var executeCmd *exec.Cmd
executeCmd = exec.Command(ok, args...)
if dir!=""{
executeCmd.Dir = dir
}
var outString []byte
outString, err = executeCmd.CombinedOutput()
if err != nil {
fmt.Println(ErrCmdFail)
return ErrCmdFail
}
fmt.Println(string(outString))
return nil
}
任意目录下执行系统命令,主要是git 命令需要:git clone、git commit 什么的。
ui 层用法示例
package service
import (
"errors"
"strings"
"github.com/olebedev/config"
)
var (
ErrParseYamlFileFail = errors.New("parse yaml file error")
ErrParseYamlKeyFail = errors.New("parse file key no exist")
)
func ConsistBody(key string, fileName string) (map[string]interface{}, error) {
var (
cfg *config.Config
err error
)
if !judgeYamlFile(fileName) {
return nil, ErrParseYamlFileFail
}
cfg, err = config.ParseYamlFile(fileName)
if err != nil {
return nil, ErrParseYamlFileFail
}
var (
cfgParam *config.Config
)
cfgParam, err = cfg.Get(key)
if err != nil {
return nil, ErrParseYamlKeyFail
}
var body = make(map[string]interface{})
body[key] = cfgParam.Root
return body, nil
}
func judgeYamlFile(fileName string) bool {
return strings.HasSuffix(fileName, ".yml")
}
用法主要是构造 body 体, 主要是为组成的 body 体可以写入文件内下。
domian 层用法示例
package roles
import (
"errors"
"fmt"
"go-example-for-live/ten/domain/constants"
"go-example-for-live/ten/infra/golang/osadapater"
"go-example-for-live/ten/ui/service"
)
var (
ErrInstanceIdExist = errors.New("config instance exist")
ErrInstaceeMakeFail = errors.New("make instance fail")
)
func CreateConfigDir(instanceId string, fullPath string) (bool, error) {
var path string
path = constants.ProjectPath + instanceId
//fmt.Println(os.Getwd())
//fmt.Println(path)
var found bool
found = osadapater.DExist(path)
if found != false {
return false, ErrInstanceIdExist
}
var err error
err = osadapater.MKdir(path) // wrong
fmt.Println(err)
if err != nil {
return false, ErrInstaceeMakeFail
}
return service.GitClone(path, "github.com", fullPath)
}
func RemoveConfigDir(instanceId string) (bool, error) {
var path string
path = constants.ProjectPath + instanceId
var err error
err = osadapater.RDir(path)
if err != nil {
return false, err
}
return true, nil
}
创建指定名称的目录,并克隆原始文件至创建的文件下。
app 层用户示例
package roles
import (
"errors"
"fmt"
"go-example-for-live/ten/domain/constants"
"go-example-for-live/ten/infra/golang/osadapater"
"go-example-for-live/ten/ui/service"
)
var (
ErrInstanceIdExist = errors.New("config instance exist")
ErrInstaceeMakeFail = errors.New("make instance fail")
)
func CreateConfigDir(instanceId string, fullPath string) (bool, error) {
var path string
path = constants.ProjectPath + instanceId
//fmt.Println(os.Getwd())
//fmt.Println(path)
var found bool
found = osadapater.DExist(path)
if found != false {
return false, ErrInstanceIdExist
}
var err error
err = osadapater.MKdir(path) // wrong
fmt.Println(err)
if err != nil {
return false, ErrInstaceeMakeFail
}
return service.GitClone(path, "github.com", fullPath)
}
func RemoveConfigDir(instanceId string) (bool, error) {
var path string
path = constants.ProjectPath + instanceId
var err error
err = osadapater.RDir(path)
if err != nil {
return false, err
}
return true, nil
}
定义实例结构体,具备实例化的方法,可以创建指定名称的文件,并克隆初始配置文件至该文件下。
主函数入口
package main
import "go-example-for-live/ten/app"
func main() {
NewApp := app.NewConfigInstance("2")
NewApp.Instance("git@github.com:wuxiaoxiaoshen/Resume.git")
}
指定名称和原始库的配置地址。
最后会在ConfigResume 创建一个名词为 2 的文件夹,克隆 Resume 这个工程,并把cfg.yml 文件分拆,写入 2/Resume 文件夹的各个文件内。
再会,本次的讲解大概就到这。