Come strutturare un'applicazione Go, progettata in base all'architettura pulita

8

Sto provando a costruire un progetto usando l'architettura pulita, come descritto qui . Ho trovato un grande articolo su come farlo in Go .

L'esempio è molto semplice e l'autore inserisce il proprio codice in pacchetti chiamati in base al livello in cui si trovano. Mi piace L'idea di zio Bob che l'architettura di un'applicazione dovrebbe comunicare chiaramente il suo intento . Quindi vorrei che la mia applicazione avesse pacchetti di livello superiore basati su aree di dominio. Quindi la mia struttura dei file sarebbe simile a questa:

/Customers
    /domain.go
    /interactor.go
    /interface.go
    /repository.go
/... the same for other domain areas

Il problema con questo è che più livelli condividono lo stesso pacchetto. Quindi non è tutto chiaro quando viene violata la regola di dipendenza, perché non hai importazioni che mostrano cosa dipende da cosa.

Vengo da uno sfondo di Python, dove questo non sarebbe un grosso problema, perché puoi importare singoli file, quindi customers.interactor potrebbe importare customers.domain .

Potremmo ottenere qualcosa di simile in go da nidificare i pacchetti, in modo che il pacchetto clienti contenga un pacchetto dominio e un pacchetto interactor, e così via. Questo sembra goffo, e i pacchetti con nomi identici possono essere noiosi da gestire.

Un'altra opzione sarebbe quella di creare più pacchetti per area del dominio. Uno chiamato customer_domain, uno chiamato customer_interactor, ecc. Ma anche questo sembra sporco. Non si adatta bene alle linee guida sui nomi dei pacchetti di Go e sembra che tutti questi pacchetti separati debbano essere raggruppati in qualche modo, poiché i loro nomi hanno un prefisso comune.

Quindi quale sarebbe un buon layout di file per questo?

    
posta bigblind 04.07.2017 - 01:41
fonte

1 risposta

4

Ci sono alcune soluzioni a questo:

  1. Separazione pacchetto
  2. Analizza analisi
  3. Analisi statica
  4. Analisi runtime

Ognuno con i suoi pro / contro.

Separazione pacchetto

Questo è il modo più semplice che non richiede la costruzione di qualcosa in più. È disponibile in due versioni:

// /app/user/model/model.go
package usermodel
type User struct {}

// /app/user/controller/controller.go
package usercontroller
import "app/user/model"
type Controller struct {}

o

// /app/model/user.go
package model
type User struct {}

// /app/controller/user.go
package controller
import "app/user/model"

type User struct {}

Ciò tuttavia rompe l'interezza del concetto di User . Per capire o modificare User devi toccare diversi pacchetti.

Tuttavia, ha una buona proprietà che è più ovvio quando model importa controller , e ad alcune estensioni viene applicata dalla semantica del linguaggio.

Analisi analisi

Se l'applicazione non è grande (meno di 30KLOC) e hai dei buoni programmatori, di solito non è necessario creare nulla. Organizzare strutture basate sul valore sarà sufficiente, ad esempio:

// /app/user/user.go
package user
type User struct {}
type Controller struct {}

Spesso le "violazioni dei vincoli" sono di scarsa importanza o facili da correggere. Ciò nuoce alla chiarezza e alla comprensibilità, a patto che non ti sfugga di mano, non devi preoccupartene.

Analisi statiche / di runtime

Puoi anche utilizzare l'analisi statica o runtime per trovare questi errori, tramite annotazioni:

statico:

// /app/user/user.go
package user

// architecture: model
type User struct {}

// architecture: controller
type Controller struct {}

dinamica:

// /app/user/user.go
package user

import "app/constraint"

var _ = constraint.Model(&User{})
type User struct {}

var _ = constraint.Controller(&Controller{})
type Controller struct {}

// /app/main.go
package main

import "app/constraint"

func init() { constraint.Check() }

Sia statico / dinamico può essere fatto anche tramite i campi:

// /app/user/user.go
package user

import "app/constraint"

type User struct {   
    _ constraint.Model
}

type Controller struct {
    _ constraint.Controller
}

Ovviamente la ricerca di queste cose diventa più complicata.

Altre versioni

Tali approcci possono essere utilizzati altrove, non solo i vincoli di tipo, ma anche la denominazione delle funzioni, le API ecc.

link

    
risposta data 06.07.2017 - 13:03
fonte

Leggi altre domande sui tag