Package Provides Something
A package provides tools for solving a particular problem.
It's important to understand that a package in Go is not a container for arbitrary code that shares some commonalities. It's not a container in the usual meaning of the word. Instead, a package is a provider of a way for accomplishing a set of tasks.
Think about how a library makes its way in to a project. There is a task to solve in the first place, and it is natural to seek for an existing solution first. Assuming a successful search, at the end of the process you find a library that does what you need, or significantly simplifies the task. So the library gives, or provides, something that you can use directly or as a building block. Notice that what you're searching for is not particular contents; it's rather a way, a solution, a tool to address a particular need. This illustrates that libraries (and not only in Go) for us, users, are providers, not collections.
That insight suggests what the author of a library should be focusing on. As an author, you design a package to provide the users of the package with ways for accomplishing a particular, well-defined set of tasks.
Yet the vast majority of packages seen in private codebases is still just collections of something. It's very common and sad to see packages that contain things, either literally, or by design. That's usually due to lack of understanding of the purpose of a package, combined with lack of design. Refrain from approaching problems in such a way.
The following example may sound corny, however it's worth repeating. A package named tools is a bad idea, the reasons given above tell us why. Such a package is a collection of usually arbitrary utility code that is either hard to decouple from its dependencies, or it has too wide, or sometimes too narrow, use case, so that it can't be put into a more specific and focused package. Thus, reject the idea of having a package like this. Instead, think about the following:
- how to make code less dependent on details so it can be moved into a focused package that provides something
- or, keep the code where it's used
- because it's unlikely to be needed somewhere else
- or because more often than not duplication is cheaper than a wrong abstraction.
If the reason for creating the tools package is to share code between other packages, ask the following questions:
- why can't I import the package that contains code that I want to use? The answer may suggest that there's a bigger and deeper problem with the design that prevents you from using it, thus it should be solved first. For example, dependencies pointing into a wrong direction might signal violation of The Dependency Inversion Principle (DIP).
- shouldn't the code, that is going to be shared, belong in the type it operates on, as a method? Then both places, the method, and the caller, would use code naturally, without a separate package.
- is there another way to address the issue?
On the other side, the model package makes a lot of sense, as long as it provides the models that represent the data structures and components of a business process. Each model encapsulates its behaviour, and the consuming packages (those that operate on models) can describe a business process using the behaviour of a model.
Be sure the package you're about to create will provide something. That something comes in handy for the next step in planning a package.