Hello!
@Kenny, if you allow me to say that you have 'right' on some points. I believe that we should have some 'common rules' about package naming but it's totally personal decision which depends on the needs of the package or the app you developing.
I do agree with a large number of the author's points, not all of them, we can't say explicit 'DONT USE THAT', they are just recommendation, nothing more, right?
If I may, let me put some words from my experience on very big packages with many dependencies, believe me, I tried a lot of designs to solve my issues and I believe that the 'big' question is NOT 'where' to put the packages because developers ,eventually, soon or later can understand where to put the packages, it's a matter of app architecture and we have many examples to learn from if we are not sure 'where' to put a package.
At my opinion the real question when designing an api's language(package naming, structs, funcs and so on) with golang(and general) is the 'singular' vs 'plural'. I know it can sound like a 'slight detail' but we are all come to this moment when we are thinking 'is it right to keep it plural but is it right to rename it singular? and reverse'.
So I came up with one 'solution':
- When all structs inside a package have a 'common interface', they are all tasks or adaptors or drivers for example, and they do the same thing or trying to solve a specific thing with the same 'policies' and they are NOT depend between their selves (a task1 struct cannot depend on task2 struct) and the client/user is allowed to use more than one of these, then the package name 'should' be plural (: 'tasks', 'adaptors' ,'drivers'). The rest of the packages 'should' be named 'singular'.
More:
- A collection of the same object and the object itself 'should' be inside a 'singular' package name, for example user collection shouldn't be named UsersCollection and it 'should' be inside a 'user' package, so we can name it like 'Collection' and refer to it like 'user.Collection' which will return a user collection ( or a collection of users, point of perspective).
- @Kenny, at your example you use 'users.Find/New` I think (my personal opinion, nothing more, nothing less) that it will be better to be named as 'user.Find -> find a User`, `user.New -> create new User` , `user.SelectMany(procedure func(...)) -> collect and return a collection of User` but as we told, it depends. For example here: https://github.com/kennygrant/gohackernews/blob/master/src/comments/actions/create.go#L24 I see 'view.New' and 'comments.New', 'view' is coming from 'fragmenta' and 'comments' from the app itself, isn't it confusing to have different style for the same thing (:create and return a Single object)?
- Plural package naming when the client(user) is able/can use more than one 'features/structs' from the package for the same scope, example name: 'plugins'. This may looks like conflict with the first of my recommendations, but it's not(think it).
- As the author says, and I agree, we should avoid using so generic package names but sometimes we need to or we don't care because they aren't used by the client, so things like utilities and commons 'should' be named singular (:'util', 'common')
- Prefer to use 'user' instead of 'models/user, services/user', so you can refer 'user.Service, user.User', yes 'user.User' seems 'bad' name but it's expected name from client. But.. wait, so we can't have a package named 'services' ? No, no we still can (if 'services' example name is confusing you can think other name, read below and you'll understand what I am 'trying' to explain) but there we 'should' put services that should work as 'middleware' or 'adaptor' between/for all other 'services-like' structs inside parent packages (:'user.Service', 'post.Service') and they, of course, should be not depend on these structs('user.Service', 'post.Service').
- Ok, what about a service which should work with both 'user' and 'post', where we 'should' to put that? The answer depends on your schema, if the user is the parent of/owns the 'post' then I recommend putting that in the 'user' package, but avoid that too: try to minimize the dependencies inside your app, so we can create a totally new package which will 'bridge' the 'user' package's features/responsibilities with the 'post' package.
Final notes:
- Try to design your internal application like you design an open-source LIBRARY for other developers.
- Think the 'client/user' side before start writing the 'server' code(: think how you would prefer to call that package's struct/function)
Again, my personal opinions. Sorry for my writing style, I'm not a native english speaker, if I didn't make things clear for something or if you thing that I 'miss' the point, please leave a comment below!
Thanks,
kataras