←back to thread

257 points pmig | 7 comments | | HN request time: 0.02s | source | bottom
Show context
bryancoxwell ◴[] No.43096481[source]
> But there are obviously work around solutions in the Go ecosystem. It uses the Context ctx, which we pass around functions in order to juggle data around in the application.

Man. This works. The context API allows/enables it. But I’d really recommend against passing data to functions via context. The biggest selling point of Go to me is that I can usually just look at anyone’s code and know what it’s doing, but this breaks down when data is hidden inside a context. Dependency injection is entirely possible without using the context package at all, interfaces are great for it.

replies(8): >>43096604 #>>43096796 #>>43096956 #>>43097757 #>>43098179 #>>43098205 #>>43099616 #>>43099625 #
MrDarcy ◴[] No.43096604[source]
I hit this point in tfa and had the same comment. Please don’t pass things around in a Comtext. Maybe stash a slog logger in there, but that’s about it.

I made the switch to Go a few years ago. For those who are on a similar journey as the author, or the author himself, I suggest spending time with the Go standard library and tools written by Rob Pike and Russ Cox to get a handle on idiomatic Go.

It’s clear the author still thinks in Java, not go. Saying Context ctx for example instead of ctx context.Context. Also DI, which is arguably not necessary at all in Go given how elegantly interfaces work.

I spent quite a lot of time using wire for DI in go only to really study the code it was generating and realizing it truly is code I would normally just write myself.

Edit:

Regarding stack traces, it turns out you don’t need them. I strongly suggest a top level error handler in Go combined with a custom error struct that records the file and line the error was first seen in your code. Then wrap the error as many times as you want to annotate additional lines as the error is handled up to the top level, but only that first point in our own code is what actually matters nearly all of the time.

replies(14): >>43096842 #>>43098162 #>>43098528 #>>43099235 #>>43099768 #>>43099821 #>>43100046 #>>43100754 #>>43100817 #>>43101015 #>>43101030 #>>43101721 #>>43101893 #>>43103377 #
1. andreasmetsala ◴[] No.43099821[source]
> Also DI, which is arguably not necessary at all in Go given how elegantly interfaces work. > I spent quite a lot of time using wire for DI in go only to really study the code it was generating and realizing it truly is code I would normally just write myself.

DI is the idea that you should create your dependencies outside of the module / class / function that uses it and pass it in. This makes it easy to swap implementations.

DI does not require any framework and I would argue you can’t write modular code without it. Most likely you are doing DI even in your manually written code.

replies(1): >>43099943 #
2. RussianCow ◴[] No.43099943[source]
If you're doing "dependency injection" by just passing arguments to functions/modules, you're not really doing dependency injection—you're doing "dependencies" without the "injection" part. I'm not saying that DI necessitates a ton of magic, but you need at least a small framework for specifying dependencies and injecting them into your modules dynamically.
replies(5): >>43100207 #>>43100273 #>>43100588 #>>43100709 #>>43101162 #
3. com2kid ◴[] No.43100207[source]
Design patterns are independent of the implementation technology.

OO and virtual functions can be implemented in C by looking up function pointers in a table.

Reference counting can be done by manually incrementing and decrementing references.

At the end of the day everything is compiled to assembly and the CPU doesn't care what ideology was in the programmer's head, except however much a given paradigm abstracts too far away from the underlying machine.

4. cryptos ◴[] No.43100273[source]
But that "framework" could be a simple factory function.
5. lucumo ◴[] No.43100588[source]
No, that's wrong.

DI requires that the deps come from outside, not that it's dynamically created. The opposite is that dependencies are created inside the unit. DI is about which part of the code owns the dependency. With DI it's some parent component, without DI it's the component itself.

DI with magic can simplify the management of component lifecycles, but it's entirely possible to do it without.

6. gf000 ◴[] No.43100709[source]
No, you can pass (inject) the necessary dependencies to the constructor of an object at creation time, and then simply use that object instance everywhere. The only thing frameworks do is "solve" the dependency graph and instantiate stuff in the correct order.

This is also the most common/preferred way Spring et alia implements their "framework-aided" DI, so that you can write unit tests easily without bootstrapping a Spring context (and it is just well-designed vanilla Java code).

7. simiones ◴[] No.43101162[source]
"Injection" in "dependency injection" simply refers to getting your dependencies from outside instead of building them yourself.

Let's take the case of an object which represents a simple CRUD web service that needs to talk to a database to get some data. Here is what it looks like without dependency injection:

  func NewService(databaseHostname, databaseLoginSecret string) (WebService, error) {
    databaseConn, err := databse.NewConn(databaseHostname, databaseLoginSecret)
    if err != nil {
        return WebService{}, fmt.Errorf("Failed to create database conn for WebService: %w", err)
    }
    return WebService{
        databaseConn: databaseConn
    }, nil
  }
And here is what it looks like with dependncy injection:

  func NewService(databaseConn DatabaseConn) WebService {
    return WebService{
        databaseConn: databaseConn
    }
  }
This is the only concept: don't build your own dependencies, get them from outside.

Ideally then there is a place in your application, possibly in `main()`, where all of the base services are initialized, in the required order, and references are passed between them as needed. This can include "factory" style objects if some of these need to be initialized on-demand.