wait[
    {
        "method": "controller:method1",
        "params": {},
        "id": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 2,
        "wait": 1
    }
]package main
import (
	"fmt"
	"sync"
	"time"
)
type chain struct {
	Req       *req
	Wait      chan bool
	Signal    chan bool
	IsWaiting bool
}
type req struct {
	Method func()
	ID     int
	W      int
}
func main() {
	chainigs := make(map[int]*chain)
	var fncs []*req
	r1 := &req{
		Method: func1,
		ID:     1,
		W:      0,
	}
	r2 := &req{
		Method: func2,
		ID:     2,
		W:      1,
	}
	r3 := &req{
		Method: func3,
		ID:     3,
		W:      0,
	}
	fncs = append(fncs, r1, r2, r3)
	for _, req := range fncs {
		ch := &chain{}
		ch.Req = req
		signalChan := make(chan bool)
		ch.Signal = signalChan
		chainigs[req.ID] = ch
		if waitChan, ok := chainigs[req.W]; ok {
			ch.Wait = waitChan.Signal
			ch.IsWaiting = true
		} else {
			ch.IsWaiting = false
		}
		chainigs[req.ID] = ch
	}
	var wg sync.WaitGroup
	for _, schain := range chainigs {
		wg.Add(1)
		go func(c *chain) {
			if c.IsWaiting {
				select {
				case <-c.Wait:
					c.Req.Method()
					wg.Done()
					go func() {
						c.Signal <- true
						close(c.Signal)
					}()
				}
			} else {
				c.Req.Method()
				wg.Done()
				c.Signal <- true
			}
		}(schain)
	}
	wg.Wait()
}
func func1() {
	time.Sleep(2 * time.Second)
	fmt.Println("FORM 1")
}
func func2() {
	fmt.Println("FROM 2")
}
func func3() {
	time.Sleep(2 * time.Second)
	fmt.Println("FROM 3")
}chain имеет два поля с каналами Wait и Signal. Wait - сдержит канал Signal другой структуры, выполение которой нужно ожидать. Ну а дальше запуск в цикле гороутин, и проверяем, если chain должен ожидать выполнение другого, то ждем сигнала. Если нет, то просто выполняем функцию и посылаем завершающий сигнал. При такой реализации удается строить цепочки вызовов, и в целом программа работает правильно. Меня интересует насколько правильным можно считать такой подход? Возможно, есть паттерны намного лучше и красивей для реализации такой возможности?  
  
  [
    {
        "method": "controller:method1",
        "params": {},
        "id": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 2,
        "wait": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 3,
        "wait": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 4,
        "wait": 2
    }
    {
        "method": "controller:method2",
        "params": {},
        "id": 5
    }
][
  [
    {
        "method": "controller:method1",
        "params": {},
        "id": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 5
    }  
  ],
  [
    {
        "method": "controller:method2",
        "params": {},
        "id": 2,
        "wait": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 3,
        "wait": 1
    },
  ],
  [
    {
        "method": "controller:method2",
        "params": {},
        "id": 4,
        "wait": 2
    }  
  ]
]for _, step := range steps {
  for _, f : range step {
    f.method(f.params)
  }
}