Lesson 20 · 20 of 20
Project: processing orders · Go
A project that brings Go topics together: orders in JSON, a generic function that works in parallel with goroutines, and a type with String.

The program receives orders as JSON, adds VAT to them in parallel and prints them. It is small, but it combines four pieces of the course: JSON tags, generics, goroutines with channels, and interfaces.
Order has the tags json:"id", json:"net" and json:"total", and `json.Unmarshal` uses them to fill the orders slice from the text. The error is printed on the first line, <nil> if all went well; in a real program, if it were not nil, you would stop there.
`ParallelMap[T, U any]` is generic: it works with any slice and any function. It starts one goroutine per item and each one writes to its own position, out[i], so the order is kept whichever finishes first. To know when they are all done, each goroutine sends true on the done channel and the function receives as many signals as there are items: the same as a WaitGroup, made with a channel.
The function passed in is a literal, func(o Order) Order, which gets a copy of the order, sets its Total (net plus 21%) and returns it. orders is left unchanged and billed holds the invoiced orders. Go works out on its own that T and U are Order.
Printing them only takes fmt.Println(o): Order has a String method, so it satisfies `fmt.Stringer` and prints as #101 120.00 -> 145.20. Natural next steps: VAT by country, the result as JSON with json.Marshal(billed), or serving it from an HTTP handler, as the Web (Go) target in DMarlon Studio does.
Code · main.go
package mainimport ( "encoding/json" "fmt")type Order struct { ID int `json:"id"` Net float64 `json:"net"` Total float64 `json:"total"`}func (o Order) String() string { return fmt.Sprintf("#%d %6.2f -> %6.2f", o.ID, o.Net, o.Total) }func ParallelMap[T, U any](in []T, f func(T) U) []U { // one goroutine per item, order kept out, done := make([]U, len(in)), make(chan bool) for i := range in { go func(i int) { out[i] = f(in[i]); done <- true }(i) } for range in { <-done // wait for one signal per goroutine } return out}func main() { var orders []Order err := json.Unmarshal([]byte(`[{"id":101,"net":120},{"id":102,"net":80},{"id":103,"net":45.5}]`), &orders) fmt.Println("decoded", len(orders), "orders, error:", err) billed := ParallelMap(orders, func(o Order) Order { o.Total = o.Net * 1.21; return o }) for _, o := range billed { fmt.Println(o) // fmt finds the String method: Order is a fmt.Stringer }}Output · go run main.go
decoded 3 orders, error: <nil> #101 120.00 -> 145.20 #102 80.00 -> 96.80 #103 45.50 -> 55.05
Key points
- JSON tags link the keys to the struct's fields.
- A generic function with goroutines works for any type.
- Writing to
out[i]keeps the order without locks. - Receiving one signal per goroutine is the same as waiting on a
WaitGroup. - A
Stringmethod decides how the type prints.
Exercise
Add a Country field (json:"country") to Order and a map with the VAT for ES, DE and FR, put a country on each order in the JSON and use the matching VAT. At the end, print billed as JSON with json.Marshal.