library or framework

which way to plug it in

SEAN K.H. LIAO

library or framework

which way to plug it in

library or framework

Occasionally, I'm in the mood to write a small utility: maybe a webhook handler to automate something, or a web page to display some information. These are mostly all applications that work over HTTP(S), so, after 2 or 3 of these, I usually start thinking about factoring the common parts into a library, and for that I've bounced between using a library style and a framework style.

Framework would be like my previous iteration go.seankhliao.com/svcrunner, where you wire up everything into the framework and hand it control to run everything. This is quite concise, with minimal boilderplate in application, but at times I found it limiting when I wanted to do something I didn't previously plan for, like hook into a different part of the application lifecycle.

Example:

1func main() {
2        hs := &http.Server{}
3        svr := server.New(hs)
4        svcrunner.Options{}.Run(
5                svcrunner.NewHTTP(hs, svr.Register, svr.Init),
6        )
7}

What I currently do with v2 at go.seankhliao.com/svcrunner/v2/tshttp might at first flance look similar, still with New and Run functions, but it leaves it up to the application to wire things up. This is much more boilerplate at each callsite, but crucially doesn't block me from doing anything. (Though I did pull in a different framework like package in github.com/google/subcommands...)

Example:

 1
 2func (c *Cmd) Execute(ctx context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
 3        err := New(ctx, c).Run(ctx)
 4        if err != nil {
 5                return subcommands.ExitFailure
 6        }
 7        return subcommands.ExitSuccess
 8}
 9
10func New(ctx context.Context, c *Cmd) *Server {
11        svr := tshttp.New(ctx, c.tshttpConf)
12        s := &Server{
13                o:   svr.O,
14                svr: svr,
15
16                render: webstyle.NewRenderer(webstyle.TemplateCompact),
17                dir:    c.dir,
18        }
19
20        svr.Mux.Handle("/eur", otelhttp.NewHandler(s.hView("eur"), "hView - eur"))
21        svr.Mux.Handle("/gbp", otelhttp.NewHandler(s.hView("gbp"), "hView - gbp"))
22        svr.Mux.Handle("/twd", otelhttp.NewHandler(s.hView("twd"), "hView - twd"))
23        svr.Mux.Handle("/", otelhttp.NewHandler(http.HandlerFunc(s.hIndex), "hIndex"))
24        svr.Mux.HandleFunc("/-/ready", func(rw http.ResponseWriter, r *http.Request) { rw.Write([]byte("ok")) })
25        return s
26}
27
28func (s *Server) Run(ctx context.Context) error {
29        return s.svr.Run(ctx)
30}