Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactoring] Layered architecture #254

Open
wants to merge 11 commits into
base: devel
Choose a base branch
from
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

NAME := volt
SRC := $(shell find . -type d -name 'vendor' -prune -o -type f -name '*.go' -print)
VERSION := $(shell sed -n -E 's/var voltVersion = "([^"]+)"/\1/p' subcmd/version.go)
VERSION := $(shell sed -n -E 's/var VoltVersion = "([^"]+)"/\1/p' usecase/version.go)
RELEASE_LDFLAGS := -s -w -extldflags '-static'
RELEASE_OS := linux windows darwin
RELEASE_ARCH := amd64 386
20 changes: 20 additions & 0 deletions _docs/layer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

## Layered architecture

The volt commands like `volt get` which may modify lock.json, config.toml,
filesystem, are executed in several steps:

1. (UI layer): Passes subcommand arguments, lock.json & config.toml structure to Gateway layer
2. (Gateway layer): Invokes usecase(s). This layer cannot touch filesystem, do network requests, because it makes unit testing difficult
3. (Usecase layer): Modify files, do network requests, and other business logic

Below is the dependency graph:

```
UI --> Gateway --> Usecase
```

* UI only depends Gateway
* Gateway doesn't know UI
* Gateway only depends Usecase
* Usecase doesn't know Gateway
10 changes: 5 additions & 5 deletions subcmd/build.go → gateway/build.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package subcmd
package gateway

import (
"flag"
"fmt"
"os"

"github.com/vim-volt/volt/gateway/builder"
"github.com/vim-volt/volt/logger"
"github.com/vim-volt/volt/subcmd/builder"
"github.com/vim-volt/volt/transaction"
)

@@ -53,10 +53,10 @@ Description
return fs
}

func (cmd *buildCmd) Run(args []string) *Error {
func (cmd *buildCmd) Run(cmdctx *CmdContext) *Error {
// Parse args
fs := cmd.FlagSet()
fs.Parse(args)
fs.Parse(cmdctx.Args)
if cmd.helped {
return nil
}
@@ -69,7 +69,7 @@ func (cmd *buildCmd) Run(args []string) *Error {
}
defer transaction.Remove()

err = builder.Build(cmd.full)
err = builder.Build(cmd.full, cmdctx.Config, cmdctx.LockJSON)
if err != nil {
logger.Error()
return &Error{Code: 12, Msg: "Failed to build: " + err.Error()}
4 changes: 2 additions & 2 deletions subcmd/build_test.go → gateway/build_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package subcmd
package gateway

import (
"bytes"
@@ -13,10 +13,10 @@ import (
"github.com/haya14busa/go-vimlparser"
"github.com/vim-volt/volt/config"
"github.com/vim-volt/volt/fileutil"
"github.com/vim-volt/volt/gateway/builder"
"github.com/vim-volt/volt/internal/testutil"
"github.com/vim-volt/volt/lockjson"
"github.com/vim-volt/volt/pathutil"
"github.com/vim-volt/volt/subcmd/builder"
)

// Checks:
2 changes: 1 addition & 1 deletion subcmd/builder/base.go → gateway/builder/base.go
Original file line number Diff line number Diff line change
@@ -12,10 +12,10 @@ import (

"github.com/hashicorp/go-multierror"
"github.com/vim-volt/volt/fileutil"
"github.com/vim-volt/volt/gateway/buildinfo"
"github.com/vim-volt/volt/lockjson"
"github.com/vim-volt/volt/logger"
"github.com/vim-volt/volt/pathutil"
"github.com/vim-volt/volt/subcmd/buildinfo"
)

// BaseBuilder is a base struct which all builders must implement
18 changes: 7 additions & 11 deletions subcmd/builder/builder.go → gateway/builder/builder.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
package builder

import (
"github.com/pkg/errors"
"os"

"github.com/pkg/errors"

"github.com/vim-volt/volt/config"
"github.com/vim-volt/volt/gateway/buildinfo"
"github.com/vim-volt/volt/lockjson"
"github.com/vim-volt/volt/logger"
"github.com/vim-volt/volt/pathutil"
"github.com/vim-volt/volt/subcmd/buildinfo"
)

// Builder creates/updates ~/.vim/pack/volt directory
type Builder interface {
Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos) error
Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos, lockJSON *lockjson.LockJSON) error
}

const currentBuildInfoVersion = 2

// Build creates/updates ~/.vim/pack/volt directory
func Build(full bool) error {
// Read config.toml
cfg, err := config.Read()
if err != nil {
return errors.Wrap(err, "could not read config.toml")
}

func Build(full bool, cfg *config.Config, lockJSON *lockjson.LockJSON) error {
// Get builder
blder, err := getBuilder(cfg.Build.Strategy)
if err != nil {
@@ -75,7 +71,7 @@ func Build(full bool) error {
}
}

return blder.Build(buildInfo, buildReposMap)
return blder.Build(buildInfo, buildReposMap, lockJSON)
}

func getBuilder(strategy string) (Builder, error) {
10 changes: 2 additions & 8 deletions subcmd/builder/copy.go → gateway/builder/copy.go
Original file line number Diff line number Diff line change
@@ -10,12 +10,12 @@ import (

"github.com/hashicorp/go-multierror"
"github.com/vim-volt/volt/fileutil"
"github.com/vim-volt/volt/gateway/buildinfo"
"github.com/vim-volt/volt/gitutil"
"github.com/vim-volt/volt/lockjson"
"github.com/vim-volt/volt/logger"
"github.com/vim-volt/volt/pathutil"
"github.com/vim-volt/volt/plugconf"
"github.com/vim-volt/volt/subcmd/buildinfo"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
@@ -25,19 +25,13 @@ type copyBuilder struct {
BaseBuilder
}

func (builder *copyBuilder) Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos) error {
func (builder *copyBuilder) Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos, lockJSON *lockjson.LockJSON) error {
// Exit if vim executable was not found in PATH
vimExePath, err := pathutil.VimExecutable()
if err != nil {
return err
}

// Read lock.json
lockJSON, err := lockjson.Read()
if err != nil {
return errors.New("could not read lock.json: " + err.Error())
}

// Get current profile's repos list
reposList, err := lockJSON.GetCurrentReposList()
if err != nil {
8 changes: 2 additions & 6 deletions subcmd/builder/symlink.go → gateway/builder/symlink.go
Original file line number Diff line number Diff line change
@@ -11,30 +11,26 @@ import (

"gopkg.in/src-d/go-git.v4"

"github.com/vim-volt/volt/gateway/buildinfo"
"github.com/vim-volt/volt/gitutil"
"github.com/vim-volt/volt/lockjson"
"github.com/vim-volt/volt/logger"
"github.com/vim-volt/volt/pathutil"
"github.com/vim-volt/volt/plugconf"
"github.com/vim-volt/volt/subcmd/buildinfo"
)

type symlinkBuilder struct {
BaseBuilder
}

// TODO: rollback when return err (!= nil)
func (builder *symlinkBuilder) Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos) error {
func (builder *symlinkBuilder) Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos, lockJSON *lockjson.LockJSON) error {
// Exit if vim executable was not found in PATH
if _, err := pathutil.VimExecutable(); err != nil {
return err
}

// Get current profile's repos list
lockJSON, err := lockjson.Read()
if err != nil {
return errors.Wrap(err, "could not read lock.json")
}
reposList, err := lockJSON.GetCurrentReposList()
if err != nil {
return err
File renamed without changes.
42 changes: 42 additions & 0 deletions gateway/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package gateway

import (
"flag"

"github.com/vim-volt/volt/config"
"github.com/vim-volt/volt/lockjson"
)

var cmdMap = make(map[string]Cmd)

// LookUpCmd looks up subcommand by name.
func LookUpCmd(cmd string) Cmd {
return cmdMap[cmd]
}

// Cmd represents volt's subcommand interface.
// All subcommands must implement this.
type Cmd interface {
ProhibitRootExecution(args []string) bool
Run(cmdctx *CmdContext) *Error
FlagSet() *flag.FlagSet
}

// CmdContext is a data transfer object between Subcmd and Gateway layer.
type CmdContext struct {
Cmd string
Args []string
LockJSON *lockjson.LockJSON
Config *config.Config
}

// Error is a command error.
// It also has a exit code.
type Error struct {
Code int
Msg string
}

func (e *Error) Error() string {
return e.Msg
}
28 changes: 8 additions & 20 deletions subcmd/get.go → gateway/get.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package subcmd
package gateway

import (
"flag"
@@ -17,12 +17,12 @@ import (

"github.com/vim-volt/volt/config"
"github.com/vim-volt/volt/fileutil"
"github.com/vim-volt/volt/gateway/builder"
"github.com/vim-volt/volt/gitutil"
"github.com/vim-volt/volt/lockjson"
"github.com/vim-volt/volt/logger"
"github.com/vim-volt/volt/pathutil"
"github.com/vim-volt/volt/plugconf"
"github.com/vim-volt/volt/subcmd/builder"
"github.com/vim-volt/volt/transaction"

multierror "github.com/hashicorp/go-multierror"
@@ -116,31 +116,25 @@ Options`)
return fs
}

func (cmd *getCmd) Run(args []string) *Error {
func (cmd *getCmd) Run(cmdctx *CmdContext) *Error {
// Parse args
args, err := cmd.parseArgs(args)
args, err := cmd.parseArgs(cmdctx.Args)
if err == ErrShowedHelp {
return nil
}
if err != nil {
return &Error{Code: 10, Msg: "Failed to parse args: " + err.Error()}
}

// Read lock.json
lockJSON, err := lockjson.Read()
if err != nil {
return &Error{Code: 11, Msg: "Could not read lock.json: " + err.Error()}
}

reposPathList, err := cmd.getReposPathList(args, lockJSON)
reposPathList, err := cmd.getReposPathList(args, cmdctx.LockJSON)
if err != nil {
return &Error{Code: 12, Msg: "Could not get repos list: " + err.Error()}
}
if len(reposPathList) == 0 {
return &Error{Code: 13, Msg: "No repositories are specified"}
}

err = cmd.doGet(reposPathList, lockJSON)
err = cmd.doGet(reposPathList, cmdctx.Config, cmdctx.LockJSON)
if err != nil {
return &Error{Code: 20, Msg: err.Error()}
}
@@ -187,7 +181,7 @@ func (cmd *getCmd) getReposPathList(args []string, lockJSON *lockjson.LockJSON)
return reposPathList, nil
}

func (cmd *getCmd) doGet(reposPathList []pathutil.ReposPath, lockJSON *lockjson.LockJSON) error {
func (cmd *getCmd) doGet(reposPathList []pathutil.ReposPath, cfg *config.Config, lockJSON *lockjson.LockJSON) error {
// Find matching profile
profile, err := lockJSON.Profiles.FindByName(lockJSON.CurrentProfileName)
if err != nil {
@@ -203,12 +197,6 @@ func (cmd *getCmd) doGet(reposPathList []pathutil.ReposPath, lockJSON *lockjson.
}
defer transaction.Remove()

// Read config.toml
cfg, err := config.Read()
if err != nil {
return errors.Wrap(err, "could not read config.toml")
}

done := make(chan getParallelResult, len(reposPathList))
getCount := 0
// Invoke installing / upgrading tasks
@@ -255,7 +243,7 @@ func (cmd *getCmd) doGet(reposPathList []pathutil.ReposPath, lockJSON *lockjson.
}

// Build ~/.vim/pack/volt dir
err = builder.Build(false)
err = builder.Build(false, cfg, lockJSON)
if err != nil {
return errors.Wrap(err, "could not build "+pathutil.VimVoltDir())
}
2 changes: 1 addition & 1 deletion subcmd/get_test.go → gateway/get_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package subcmd
package gateway

import (
"bytes"
9 changes: 5 additions & 4 deletions subcmd/help.go → gateway/help.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package subcmd
package gateway

import (
"flag"
@@ -99,7 +99,8 @@ Command
return fs
}

func (cmd *helpCmd) Run(args []string) *Error {
func (cmd *helpCmd) Run(cmdctx *CmdContext) *Error {
args := cmdctx.Args
if len(args) == 0 {
cmd.FlagSet().Usage()
return nil
@@ -112,7 +113,7 @@ func (cmd *helpCmd) Run(args []string) *Error {
if !exists {
return &Error{Code: 1, Msg: fmt.Sprintf("Unknown command '%s'", args[0])}
}
args = append([]string{"-help"}, args[1:]...)
fs.Run(args)
cmdctx.Args = append([]string{"-help"}, args[1:]...)
fs.Run(cmdctx)
return nil
}
2 changes: 1 addition & 1 deletion subcmd/help_test.go → gateway/help_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package subcmd
package gateway

import (
"strings"
Loading