Aggiorna da v1 a v2 o v3
Aggiornare da v2 a v3
v2 e v3 hanno funzionalità equivalenti e ciò rende la procedura di upgrade abbastanza semplice. Goa v3 richiede il supporto ai moduli Go, pertanto è richiesto Go 1.11 o successivo.
Aggiornare da v2 a v3 è semplice come:
- Abilitare i Go modules sul tuo progetto (
env GO111MODULE=on go mod init
) - Aggiornare la import path del package goa package a
goa.design/goa/v3/pkg
- Aggiornare la import path del package Goa X da
goa.design/goa/X
agoa.design/goa/v3/X
Ed è fatta! Nota che anche il tool goa
nella v3 è retrocompatibile e può generare
codice a partire da design v2. Ciò rende possibile di lavorare concorrentemente
sia su progetti v2 che v3 mantenendo la v2 in GOPATH
e usando la v3 nei Go
modules.
Aggiornare da v1 a v2 o v3
Goa v2 e v3 portano molte nuove funzionalità e molti miglioramenti rispetto alla v1, in particolare:
- una architettura modulare che separa chiaramente i concetti di trasporto e logica di business
- supporto a gRPC
- meno dipendenze esterne
- un plugin system migliore, che include il supporto a Go kit
Molti dei cambiamenti sono fondamentali e hanno impatto su come vengono creati i design, tuttavia vengono seguiti gli stessi principi e value proposition:
- una singola sorgente di verità creata dai DSL dei design
- un generatore di codice che dati i DSL crea documentazione, server e client
Questo documento descrive i cambiamenti e fornisce alcune linee guida su come aggiornare.
Nota: Goa v2 e v3 sono equivalenti, l’unica differenza è che la v3 sfrutta e supporta i Go modules mentre la v2 non lo fa. Il resto di questo documento si riferisce alla v3 ma si può anche applicare per la v2.
Cambiamenti al DSL
Goa v3 promuove una netta separazione dei layer rendendo possibile progettare le API di servizio
indipendentemente dal livello d trasporto sottostante. I DSL specifici di trasporto permettono di
mappare ogni livello (HTTP o gRPC), quindi invece di Resources
e Actions
i DSL si concentrano
su Services
e Methods
. Ogni metodo descrive input e output. I DSL transport-specific descrivono
come vengono come essi vengono costruiti da richieste HTTP o messaggi gRPC e come gli output sono
construiti e scrivono risposte HTTP o messaggi gRPC in uscita.
NOTA: I DSL v3 sono documentati nei godoc.
Types
Per la maggior parte i DSL non hanno molte differenze nella definizione dei tipi:
MediaType
ora è ResultType per rendere chiaro che il tipo usato da questo DSL descrive il risultato di un metodo. Nota che i tipi standard definiti usando il DSL Type possono anche essere utilizzati per i result types.- I result types possono omettere le viste. Se lo fanno una vista di default viene creata con tutti gli attributi.
- Il nuovo DSL Field è identico a Attribute, ma permette di specificare un indice per il campo da far corrispondere al field number gRPC.
HashOf
ora è MapOf, più intuitivo per gli sviluppatori Go.- Ci sono nuovi primitivi per descrivere più precisamente il layout dei dati: Int, Int32, Int64, UInt, UInt32, UInt64, Float32, Float64 and Bytes.
- I tipi
DateTime
eUUID
vengono deprecati in favore di String e un corrispondente validazione Format.
Esempio
Questo v1 media type:
var Person = MediaType("application/vnd.goa.person", func() {
Description("A person")
Attributes(func() {
Attribute("name", String, "Name of person")
Attribute("age", Integer, "Age of person")
Required("name")
})
View("default", func() { // View defines a rendering of the media type.
Attribute("name") // Media types may have multiple views and must
Attribute("age") // have a "default" view.
})
})
Corrisponde al seguente v3 result type:
var Person = ResultType("application/vnd.goa.person", func() {
Description("A person")
Attributes(func() {
Attribute("name", String, "Name of person")
Attribute("age", Int, "Age of person")
Required("name")
})
})
O a questo v3 result type con campi esplicitamente definiti:
var Person = ResultType("application/vnd.goa.person", func() {
Description("A person")
Attributes(func() {
Field(1, "name", String, "Name of person")
Field(2, "age", Int, "Age of person")
Required("name")
})
})
API
Sono stati apportati i seguenti cambiamenti al DSL API:
- I DSL
Host
,Scheme
eBasePath
sono rimpiazzati con Server. - Il DSL Server permette di definire proprietà del server per ambienti differenti Ogni server può elencare i servizi che ospita e si possono definire più server in un unico design.
Origin
ora fa parte del plugin CORS.ResponseTemplate
eTrait
sono stati deprecati.
Esempio
Questa v1 API section:
var _ = API("cellar", func() {
Title("Cellar Service")
Description("HTTP service for managing your wine cellar")
Scheme("http")
Host("localhost:8080")
BasePath("/cellar")
})
Corrisponde alla seguente v3 section:
var _ = API("cellar", func() {
Title("Cellar Service")
Description("HTTP service for managing your wine cellar")
Server("app", func() {
Host("localhost", func() {
URI("http://localhost:8080/cellar")
})
})
})
Oppure a questa v3 section che usa più server:
var _ = API("cellar", func() {
Title("Cellar Service")
Description("HTTP service for managing your wine cellar")
Server("app", func() {
Description("App server hosts the storage and sommelier services.")
Services("sommelier", "storage")
Host("localhost", func() {
Description("default host")
URI("http://localhost:8080/cellar")
})
})
Server("swagger", func() {
Description("Swagger server hosts the service OpenAPI specification.")
Services("swagger")
Host("localhost", func() {
Description("default host")
URI("http://localhost:8088/swagger")
})
})
})
Services
La funzione Resource
ora si chiama Service. I
DSL ora sono organizzati in sezioni che non dipendono più dal livello di trasporto, la quale
elenca anche i potenziali errori ritornati dai metodi. Ci pensa il codice specifico per il trasporto
a tradurre questi errori o risultati in risposte HTTP o messaggi gRPC con gli stati corretti.
BasePath
ora è Path e appare sotto il DSL HTTP.CanonicalActionName
ora si chiama CanonicalMethod e appare sotto la DSL HTTP.Response
viene rimpiazzata da Error.Origin
ora fa parte del plugin CORS.DefaultMedia
viene deprecato.
Esempio
Questo v1 design:
Resource("bottle", func() {
Description("A wine bottle")
BasePath("/bottles")
Parent("account")
CanonicalActionName("get")
Response(Unauthorized, ErrorMedia)
Response(BadRequest, ErrorMedia)
// ... Actions
})
Equivalent v3 design:
Service("bottle", func() {
Description("A wine bottle")
Error("Unauthorized")
Error("BadRequest")
HTTP(func() {
Path("/bottles")
Parent("account")
CanonicalMethod("get")
})
// ... Methods
})
Methods
La funzione Action
viene rimpiazzata da Method. Come per i
servizi i DSL sono organizzati in sezioni agnostiche al livello di trasporto e sezioni specifiche ad essi.
Le sezioni agnostic definiscono payload e risultati così come gli errori non definiti a livello di servizio.
I DSL specifiche di trasporto mappano payload e risultati a costrutti specifici come header HTTP, corpo,
eccetera.
- La maggior parte dei DSL presenti nella v1 sono specifici per HTTP e sono stati spostati sotto il DSL HTTP.
- Param e Header ora richiedono un semplice elenco di attributi del payload o dei result type.
- Le error response ora usano il DSL Error.
- I path parameters HTTP ora sono definiti con le parentesi graffe invece dei due punti:
/foo/{id}
anziché/foo/:id
.
Mapping di input e output
Questo esempio di v1 action design:
Action("update", func() {
Description("Change account name")
Routing(
PUT("/:accountID"),
)
Params(func() {
Param("accountID", Integer, "Account ID")
})
Payload(func() {
Attribute("name", String, "Account name")
Required("name")
})
Response(NoContent)
Response(NotFound)
Response(BadRequest, ErrorMedia)
})
Equivale al seguente v3 design:
Method("update", func() {
Description("Change account name")
Payload(func() {
Attribute("accountID", Int, "Account ID")
Attribute("name", String, "Account name")
Required("name")
})
Result(Empty)
Error("NotFound")
Error("BadRequest")
HTTP(func() {
PUT("/{accountID}")
})
})