Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>There're two high-level pieces to this: figuring out which code goes in which package, and tweaking your APIs to reduce the need for packages to take on as many dependencies. </p> <p>On designing APIs that avoid the need for some imports:</p> <ul> <li><p>Write config functions for hooking packages up to each other <em>at run time rather than compile time</em>. Instead of <code>routes</code> importing all the packages that define routes, it can <em>export</em> <code>routes.Register</code>, which <code>main</code> (or code in each app) can call. In general, configuration info probably flows from <code>main</code> or a dedicated package; you don't want it scattered throughout your app.</p></li> <li><p><em>Pass around basic types and <code>interface</code> values.</em> If you're depending on a package for just a type name, maybe you can avoid that. Maybe some code handling a <code>[]Page</code> can get instead use a <code>[]string</code> of filenames or a <code>[]int</code> of IDs or some more general interface (<code>sql.Rows</code>) instead. </p></li> <li><p><em>Consider having 'schema' packages with just pure data types and interfaces,</em> so <code>User</code> is separate from code that might load users from the database. It doesn't have to depend on much (maybe on anything), so you can include it from anywhere. <a href="https://go-talks.appspot.com/github.com/benbjohnson/structuring-applications-for-growth/main.slide#1" rel="noreferrer">Ben Johnson gave a lightning talk at GopherCon 2016</a> suggesting that and organizing packages by dependencies.</p></li> </ul> <p>On organizing code into packages:</p> <ul> <li><p>As a rule, <em>split a package up when each piece could be useful on its own</em>. If two pieces of functionality are really intimately related, you don't have to split them into packages at all; you can organize with multiple files or types instead. Big packages can be OK; Go's <code>net/http</code> is one, for instance.</p></li> <li><p><em>Break up grab-bag packages (<code>utils</code>, <code>tools</code>) by topic or dependency.</em> Otherwise you can end up importing a huge <code>utils</code> package (and taking on all its dependencies) for one or two pieces of functionality (that wouldn't have so many dependencies if separated out).</p></li> <li><p><em>Consider pushing reusable code 'down' into lower-level packages untangled from your particular use case.</em> If you have a <code>package page</code> containing both logic for your content management system and all-purpose HTML-manipulation code, consider moving the HTML stuff "down" to a <code>package html</code> so you can use it without importing unrelated content management stuff. </p></li> </ul> <hr> <p>Here, I'd rearrange things so the router doesn't need to include the routes: instead, each app package calls a <code>router.Register()</code> method. This is what <a href="http://www.gorillatoolkit.org/pkg/mux" rel="noreferrer">the Gorilla web toolkit's <code>mux</code> package</a> does. Your <code>routes</code>, <code>database</code>, and <code>constants</code> packages sound like low-level pieces that should be imported by your app code and not import it.</p> <p>Generally, try to build your app in layers. Your higher-layer, use-case-specific app code should import lower-layer, more fundamental tools, and never the other way around. Here are some more thoughts:</p> <ul> <li><p>Packages are for separating <strong>independently usable bits of functionality</strong>; you don't need to split one off whenever a source file gets large. Unlike in, say, Python or Java, in Go one can split and combine and rearrange files completely independent of the package structure, so you can break up huge files without breaking up packages. </p> <p>The standard library's <code>net/http</code> is about 7k lines (counting comments/blanks but not tests). Internally, it's split into many smaller files and types. But it's one package, I think 'cause there was no reason users would want, say, just cookie handling on its own. On the other hand, <code>net</code> and <code>net/url</code> <em>are</em> separate because they have uses outside HTTP.</p> <p>It's great if you <em>can</em> push "down" utilities into libraries that are independent and feel like their own polished products, or cleanly layer your application itself (e.g., UI sits atop an API sits atop some core libraries and data models). Likewise "horizontal" separation may help you hold the app in your head (e.g., the UI layer breaks up into user account management, the application core, and administrative tools, or something finer-grained than that). But, the core point is, <em>you're free to split or not as works for you</em>.</p></li> <li><p><strong>Set up APIs to configure behavior at run-time so you don't have to import it at compile time.</strong> So, for example, your URL router can expose a <code>Register</code> method instead of importing <code>appA</code>, <code>appB</code>, etc. and reading a <code>var Routes</code> from each. You could make a <code>myapp/routes</code> package that imports <code>router</code> and all your views and calls <code>router.Register</code>. The fundamental idea is that the router is all-purpose code that needn't import your application's views.</p> <p>Some ways to put together config APIs:</p> <ul> <li><p><em>Pass app behavior via <code>interface</code>s or <code>func</code>s:</em> <code>http</code> can be passed custom implementations of <code>Handler</code> (of course) but also <code>CookieJar</code> or <code>File</code>. <code>text/template</code> and <code>html/template</code> can accept functions to be accessible from templates (in a <code>FuncMap</code>).</p></li> <li><p><em>Export shortcut functions from your package if appropriate:</em> In <code>http</code>, callers can either make and separately configure some <code>http.Server</code> objects, or call <code>http.ListenAndServe(...)</code> that uses a global <code>Server</code>. That gives you a nice design--everything's in an object and callers can create multiple <code>Server</code>s in a process and such--but it <em>also</em> offers a lazy way to configure in the simple single-server case. </p></li> <li><p><em>If you have to, just duct-tape it:</em> You don't have to limit yourself to super-elegant config systems if you can't fit one to your app: maybe for some stuff a <code>package "myapp/conf"</code> with a global <code>var Conf map[string]interface{}</code> is useful. But be aware of downsides to global conf. If you want to write reusable libraries, they can't import <code>myapp/conf</code>; they need to accept all the info they need in constructors, etc. Globals also risk hard-wiring in an assumption something will always have a single value app-wide when it eventually won't; maybe today you have a single database config or HTTP server config or such, but someday you don't.</p></li> </ul></li> </ul> <p>Some more specific ways to move code or change definitions to reduce dependency issues:</p> <ul> <li><p><strong>Separate fundamental tasks from app-dependent ones.</strong> One app I work on in another language has a "utils" module mixing general tasks (e.g., formatting datetimes or working with HTML) with app-specific stuff (that depends on the user schema, etc.). But the users package imports the utils, creating a cycle. If I were porting to Go, I'd move the user-dependent utils "up" out of the utils module, maybe to live with the user code or even above it.</p></li> <li><p><strong>Consider breaking up grab-bag packages.</strong> Slightly enlarging on the last point: if two pieces of functionality are independent (that is, things still work if you move some code to another package) <em>and</em> unrelated from the user's perspective, they're candidates to be separated into two packages. Sometimes the bundling is harmless, but other times it leads to extra dependencies, or a less generic package name would just make clearer code. So my <code>utils</code> above might be broken up by topic or dependency (e.g., <code>strutil</code>, <code>dbutil</code>, etc.). If you wind up with lots of packages this way, we've got <a href="https://github.com/bradfitz/goimports" rel="noreferrer"><code>goimports</code></a> to help manage them.</p></li> <li><p><strong>Replace import-requiring object types in APIs with basic types and <code>interface</code>s.</strong> Say two entities in your app have a many-to-many relationship like <code>User</code>s and <code>Group</code>s. If they live in different packages (a big 'if'), you can't have both <code>u.Groups()</code> returning a <code>[]group.Group</code> and <code>g.Users()</code> returning <code>[]user.User</code> because that requires the packages to import each other. </p> <p>However, you could change one or both of those return, say, a <code>[]uint</code> of IDs or a <code>sql.Rows</code> or some other <code>interface</code> you can get to without <code>import</code>ing a specific object type. Depending on your use case, types like <code>User</code> and <code>Group</code> might be so intimately related that it's better just to put them in one package, but if you decide they should be distinct, this is a way. </p></li> </ul> <p>Thanks for the detailed question and followup.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload