preslavrachev / preslav.me-comments

0 stars 0 forks source link

2023/12/15/golang-interfaces-are-not-meant-for-that/ #28

Open utterances-bot opened 6 months ago

utterances-bot commented 6 months ago

Interfaces Are Not Meant for That · Preslav Rachev

I am a software engineer with a decade-long experience developing software in Python, Go, and Java.

https://preslav.me/2023/12/15/golang-interfaces-are-not-meant-for-that/

croked91 commented 6 months ago

Thank you for this article. It's really interesting, but how we should make DI without interfaces?

preslavrachev commented 6 months ago

@croked91 Great question. At its core dependency injection says nothing about mocking, or whether you should satisfy your dependencies via interfaces. DI is about how components obtain their dependencies not what those dependencies are. The simplest example I can present you with, is connecting to a database. By using the DI principle, we instantiate a DB connection and pass it to our other components, rather than making them implicitly create the connection themselves.

// Do this
func NewCustomerService(db *sql.DB) *CustomerService {
    // attach the DB instance at construction
    // ...
}

// Don't do this
func NewCustomerService() *CustomerService {
    // attach the DB instance at construction
    db, err := sql.Open(/*...*/)
    // ...
}

Notice that I am passing a pure sql.DB here, not an interface. However, even without an interface, I have the power to tell my components what other database to use when testing, for example. You’ll see lots of examples where people use PostgreSQL/MySQL in production, and an in-memory SQLite variant when testing. Those are extremely fast to set up and you can throw them away after each test, as they basically live in the RAM.

So, long story short, DI is about how you are passing your dependencies, not what you should be passing. It's all about being pragmatic and not over-complicating your code in the end.

Hope that makes sense.

croked91 commented 6 months ago

Great. Thank you for the answer!

sam0sva1 commented 6 months ago

Hello. Thank you for the article. Interesting thoughts.

I'm a bit confused. Here you're talking about an idea but it's not transparent how you suggest to handle entity nesting. I understand it could sound a bit silly but could you please provide some valuable examples. I have database repos, a service that works with a few repos, and a controller handler that processes incoming request data and passes it into the service. It's really interesting how you manage this kind of dependency nesting.

nicolasparada commented 6 months ago

This is great. Recently we did this. We had a large back-end system with lots of interface abstractions that really were there just to do unit-testing with mocks between the layers (http handlers, service and repository). Then we started just doing integration testing with https://github.com/ory/dockertest so we didn't have much more use for those interface and mocks. We removed all those big interfaces and used the actual implementations directly. The code is many times easier to follow now. And those unit tests with mocks were useless really since they didn't test the real thing, but just mocks.

I also agree so much with the RoundTripped example to just abstract the IO part of it. Its genius 👏👏👏

komuw commented 5 months ago

Great article.

Are you developing a library or an application? Interfaces are more likely to make use in library code than in applications.

I personally would disagree with this point(unless I misunderstood it). I would think that interfaces are best created/used by applications rather than libraries. That view also fits well with the view in; https://go.dev/wiki/CodeReviewComments#interfaces which I can summarise as; define interfaces in the consumer side not on the producer