Struggling with method design for a CLI. What is part of a client library? What is part of cobra?
Say we have an internal platform with an exposed API. This is for creating testing environments, deploying new versions, etc.
- I have a generic `makeRequest` method that's a wrapper around httpClient.Do
- I have some method wrappers around those (c.Put, c.Get, etc...)
- Then I have the final client library methods. Say for Environment (basically a service catalog) it might be:
​
func (c *Client) Environment(ctx context.Context, environmentName string) (*APIResponse, error) {
queryParams := make(map[string]string)
endpoint := fmt.Sprintf("/api/environments/%s", url.PathEscape(environmentName))
return c.Get(ctx, endpoint, queryParams)
}
The commands/subcommands we want to support are, say
- get environment {name}
- list environments
- create environment {name}
- delete environment {name}
- etc.
So far we have an `internal/` directory and a `cmd/` directory. For this given platform, we have a similar package in both. One for handling the client communication, one for the cli itself, with cobra. The cli has other packages and commands/subcommands for operations in, say, AWS/GCP, etc.
I really, really want to center around best practices, go style, etc. I could list off plenty of rules about package naming, avoiding nesting, testing, panicking, error handling... But this, fundamental architecture design... I just don't know what's best. What's going to save us the most pain in the future?
- How specific should the api "client" library be in `internal`?
- How much goes into the `cmd/` companion package? (using cobra/viper)?
- Should all of those API use cases be in the client package, or the cmd package?
I just don't know.