¿Cuál es una forma sensata de diseñar un proyecto de Go? [Cerrado]

113

Tengo un proyecto de Go que está comenzando a volverse más complejo y quiero diseñar el sistema de archivos de tal manera que reduzca el dolor.

¿Hay algunos buenos ejemplos de lo que tiene sentido?

australiano
fuente

Respuestas:

132

Actualización de mayo de 2013: la documentación oficial se encuentra en la sección " Organización del código "

El código Go debe mantenerse dentro de un espacio de trabajo .
Un espacio de trabajo es una jerarquía de directorios con tres directorios en su raíz:

  • src contiene archivos de origen de Go organizados en paquetes (un paquete por directorio),
  • pkg contiene objetos de paquete, y
  • bin contiene comandos ejecutables.

El go toolconstruye paquetes fuente e instala los binarios resultantes en los directorios pkgy bin.

El srcsubdirectorio normalmente contiene varios repositorios de control de versiones (como Git o Mercurial) que rastrean el desarrollo de uno o más paquetes fuente.

bin/
    streak                         # command executable
    todo                           # command executable
pkg/
    linux_amd64/
        code.google.com/p/goauth2/
            oauth.a                # package object
        github.com/nf/todo/
            task.a                 # package object
src/
    code.google.com/p/goauth2/
        .hg/                       # mercurial repository metadata
        oauth/
            oauth.go               # package source
            oauth_test.go          # test source

Actualización de julio de 2014: consulte " Estructuración de aplicaciones en Go " de Ben Johnson

Ese artículo incluye consejos como:

Separe su binario de su aplicación

combinar el main.goarchivo y la lógica de mi aplicación en el mismo paquete tiene dos consecuencias:

  • Hace que mi aplicación sea inutilizable como biblioteca.
  • Solo puedo tener una aplicación binaria.

La mejor manera que he encontrado para solucionar esto es simplemente usar un cmddirectorio " " en mi proyecto donde cada uno de sus subdirectorios es un binario de aplicación.

camlistore/
  cmd/
    camget/
      main.go
    cammount/
      main.go
    camput/
      main.go
    camtool/
      main.go

Desarrollo impulsado por bibliotecas

Mover el main.goarchivo fuera de su raíz le permite construir su aplicación desde la perspectiva de una biblioteca. El binario de su aplicación es simplemente un cliente de la biblioteca de su aplicación.

A veces, es posible que desee que los usuarios interactúen de varias formas para crear varios binarios.
Por ejemplo, si tiene un adderpaquete " " que permite a los usuarios sumar números, es posible que desee lanzar una versión de línea de comandos, así como una versión web.
Puede hacer esto fácilmente organizando su proyecto de esta manera:

adder/
  adder.go
  cmd/
    adder/
      main.go
    adder-server/
      main.go

Los usuarios pueden instalar los binarios de la aplicación "sumadora" con "go get" utilizando puntos suspensivos:

$ go get github.com/benbjohnson/adder/...

¡Y listo, tu usuario tiene “ adder” y “ adder-server” instalados!

No te vuelvas loco con los subpaquetes

Por lo general, los tipos de mi proyecto están muy relacionados, por lo que se ajusta mejor desde el punto de vista de la usabilidad y la API.
Estos tipos también pueden aprovechar las llamadas no exportadas entre ellos, lo que mantiene la API pequeña y clara.

  1. Agrupe tipos y códigos relacionados en cada archivo. Si sus tipos y funciones están bien organizados, entonces encuentro que los archivos tienden a tener entre 200 y 500 SLOC. Esto puede parecer mucho, pero me resulta fácil navegar. 1000 SLOC suele ser mi límite superior para un solo archivo.
  2. Organice el tipo más importante en la parte superior del archivo y agregue tipos de importancia decreciente hacia la parte inferior del archivo.
  3. Una vez que su aplicación comience a superar los 10,000 SLOC, debe evaluar seriamente si se puede dividir en proyectos más pequeños.

Nota: esa última práctica no siempre es buena:

Lo siento, no puedo estar de acuerdo con esta práctica.
Separar el tipo de archivos ayuda a la gestión del código, la legibilidad, la capacidad de mantenimiento y la capacidad de prueba.
También puede garantizar la responsabilidad única y el seguimiento del principio abierto / cerrado ...
La regla para no permitir la dependencia circular es forzar que tengamos una estructura clara de los paquetes.


(Alternativa de febrero de 2013, srcsolo con respecto )
Puede encontrar el diseño clásico ilustrado en " Diseño de código de GitHub ":

La aplicación y ambas bibliotecas viven en Github, cada una en su propio repositorio.
$GOPATHes la raíz del proyecto: cada uno de sus repositorios de Github se comprobará en varias carpetas a continuación $GOPATH.

Su diseño de código se vería así:

$GOPATH/
    src/
        github.com/
            jmcvetta/
                useless/
                    .git/
                    useless.go
                    useless_test.go
                    README.md
                uselessd/
                    .git/
                    uselessd.go
                    uselessd_test.go
                    README.md

Cada carpeta a continuación src/github.com/jmcvetta/es la raíz de un git checkout separado.

Sin embargo, eso atrajo algunas críticas en esta página de reddit :

Recomiendo encarecidamente no estructurar el repositorio como lo ha hecho, se romperá " go get", que es una de las cosas más útiles de Go.
Es mucho mejor escribir su código para las personas que sí conocen Go, ya que es más probable que sean ellos quienes lo compilen.
Y para las personas que no lo hacen, al menos se familiarizarán con el idioma.

Coloque el paquete principal en la raíz del repositorio.
Tenga los activos en un subdirectorio (para mantener las cosas ordenadas).
Mantenga la parte principal del código en un subpaquete (en caso de que alguien quiera reutilizarlo fuera de su binario).
Incluya un script de configuración en la raíz del repositorio para que sea fácil de encontrar.

Sigue siendo solo un proceso de dos pasos para descargar, compilar, instalar y configurar:

  • " go get <your repo path>": descarga e instala el código Go, con un subdirectorio para los elementos.
  • $GOPATH/<your repo path>/setup.sh: distribuye los activos en el lugar correcto e instala el servicio
VonC
fuente
15
Un (gran) problema setup.shes que Go es razonablemente multiplataforma, mientras que los scripts de shell POSIX no lo son.
kostix
La estructura jmcvetta no se romperá go get, ya que uselessd importa inútil, go get instalará ambos con un go get ... / uselessd. Pero estoy de acuerdo en que si inútil es una biblioteca hecha específicamente para uselessd, tiene más sentido mantenerla en un solo repositorio de git, como una subcarpeta o hermanos.
mna
@PuerkitoBio Estoy de acuerdo. Mi capacitación en control de versiones y en administración basada en componentes ( stackoverflow.com/a/933735/6309 ) me lleva más hacia un componente por repositorio, de ahí la segunda parte de esta respuesta.
VonC
7

Supongo que con 'proyecto' no te refieres a un paquete Go sino a un software que desarrollas. De lo contrario, puede obtener ayuda aquí y aquí . Sin embargo, no es tan diferente a escribir paquetes para Go: use paquetes, cree una carpeta para cada paquete y combine esos paquetes en su aplicación.

Para formarte una opinión, puedes consultar los repositorios de Go en tendencia en github: https://github.com/trending/go . Ejemplos notables son cayley y zeus .

El esquema más popular es probablemente tener un archivo Go principal y muchos módulos y submódulos en sus propios directorios. En caso de que tenga muchos metaarchivos (doc, licencia, plantillas, ...) es posible que desee poner el código fuente en un subdirectorio. Eso es lo que hice hasta ahora.

nemo
fuente
@aussiegeek, no soy un experto en Go, pero apliqué con éxito lo que nemo propuso en mi propio código: la idea es que puede tener módulos en el directorio de su proyecto, solo tiene que referirse a ellos usando su prefijo completo - relativo a $GOPATH/src, o el uso de sus go getnombres -table.
kostix
doozerdno es un buen ejemplo, incluso sus pruebas son débiles.
Inanc Gumus
@InancGumus Te animo a que sugieras un mejor ejemplo.
nemo
mira esto y esto .
Inanc Gumus
1

Existe un enfoque recomendado por los autores de Golang que define cómo diseñar su código para que funcione mejor con las herramientas de Go y sea compatible con los sistemas de control de código fuente.

FigmentEngine
fuente
1
Así es como se distribuye $GOROOT, no el código dentro del src/<project>directorio.
docwhat