Should You Use Dependency Injection Frameworks?

Поділитися
Вставка
  • Опубліковано 21 вер 2024

КОМЕНТАРІ • 104

  • @ArjanCodes
    @ArjanCodes  6 місяців тому +1

    👷 Join the FREE Code Diagnosis Workshop to help you review code more effectively using my 3-Factor Diagnosis Framework: www.arjancodes.com/diagnosis

    • @natsumizu99
      @natsumizu99 6 місяців тому

      Is the diagnosis link working right?
      I followed the link, filled the box, and ... waited for a week and got no mail. 😢

    • @shlomokallner3180
      @shlomokallner3180 3 місяці тому

      Hey @ArjanCodes ! can you review `fast_depends` in relation to this video? the creator of the library attempted to extract the FastAPI DI Framework into a separate Library for people to use without FastAPI. I wonder how you would use it in the examples of this video..

  • @proofit404
    @proofit404 6 місяців тому +20

    As an author of dependency injection library who maintain it for last 10 years I would be happy if people just care about IoC. That's would be enough.

    • @cableraywire
      @cableraywire 5 місяців тому +2

      I should know this, but I had to look up IoC to remember it was Inversion of Control

  • @kayakMike1000
    @kayakMike1000 6 місяців тому +76

    It really depends. (I just injected a Dad joke)

    • @spacebuddy5339
      @spacebuddy5339 6 місяців тому +1

      Should have left it at the joke. Explaining it was anticlimactic. Nice one though

    • @chrisbek5
      @chrisbek5 6 місяців тому

      y made my day 😂

    • @iSaac-kp5lk
      @iSaac-kp5lk 6 місяців тому

      ah!

    • @ShizoMoses
      @ShizoMoses 6 місяців тому +2

      ​@@spacebuddy5339Then again, the explanation also had a joke.

    • @thepianoaddict
      @thepianoaddict 6 місяців тому

      I (dependen)see what you did there.

  • @UNgineering
    @UNgineering 6 місяців тому +21

    Personally I wouldn't use the "inject" library for two reasons: when calling the functions, I don't know if it has the dependency injected, or doesn't at a glance, or if it has the right dependency injected; i also don't like relying on 3rd party libraries for this design-pattern-type of work - tomorrow they decide to change the way they initialize their injections, now I have to rebuild my project around that (unless I never update it).
    I do use fastAPI's dependency injection, because it explicitly states what's being injected and where.

    • @Nalewkarz
      @Nalewkarz 6 місяців тому +2

      To some point i get it, but lets be real here. EVERY third party library has that problem. Second thing is that FastAPI is just overhyped and their DI just sucks.

    • @UNgineering
      @UNgineering 6 місяців тому

      @@Nalewkarz *gasp* blasphemy! lol.
      you have a point, there's no perfect library or framework. I just found that fastapi does 90% of things that I need very well, and I cobble together the other 10%, what sold me on them is the documentation and executable examples.

    • @jval7
      @jval7 5 місяців тому +1

      Exactly, Explicit is better than implicit, check out Dependency Injector library

  • @slavoie
    @slavoie 6 місяців тому +5

    For logging, you may want to look at loguru. It's as easy to use as doing `from loguru import logger` anywhere you need it. Assuming an application has a single entry point from which initialization code is run, it's only a matter of running some setup code for loguru once and the configuration will be applied everywhere the logger is imported. Pretty neat, and entirely solves the dependency injection issue!

  • @loic1665
    @loic1665 6 місяців тому +3

    Haven't used such frameworks. I have a feeling they would make debugging harder 😆

  • @jarzez
    @jarzez 6 місяців тому +7

    I currently use spring boot in my work, and I hate the dependency injection of it.
    Coming from C/C++ background, I want to be able to see at a glance what object is being passed or created.
    Too many times some junior developer comes and asks me to solve their "bean not found" problem, if dependency injection wasn't used, they could solve it themselves because the error will be explicit.

    • @isodoubIet
      @isodoubIet 4 місяці тому +1

      This to me is just looking like global variables with extra steps.

  • @JobertoDiniz
    @JobertoDiniz Місяць тому +2

    dependency injection shines when you have a graph of dependencies. Your example is too simple and it does not require a dependency injection framework indeed. But when you want to configure graph objects that depends on particular types, dependency injection frameworks help. The FastAPI does not help much with graphs. It says it does, but it's coupled with FastAPI. If you are creating a UserRepository class, you don't want to add "Depends" function from FastApi on it. I'm using the lib "dependency_injector" which you can configure graphs of dependencies and it works really well.

  • @paxdriver
    @paxdriver 6 місяців тому +1

    Absolutely! For programs with limbs in different areas, like scrapers or manipulating user data in productivity / organising apps. I think it'd be very handy to explicitly prescribe the control flow from the server for eg.

  • @timelschner8451
    @timelschner8451 6 місяців тому +4

    I am not sure if this inject framework really does help with maintainance and readability. I definitly directly want to see what parameters is needed for a given method. Using the framework I would dig deeper to see what the method acutall needs. Is this REALLY better? I would vote NO.

    • @Lexaire
      @Lexaire 6 місяців тому +1

      Imagine you are calling a dozen functions, some of which will access your database, some of which need to access a UserRepository, some of which need to access a PostRepository, some need the MailService, and some need the SNSService. How are you going to make sure every function has the classes it needs to perform its work without passing around giant globs of service instances and creating new instances all over the place?

  • @JuanLuisCasanova-d4l
    @JuanLuisCasanova-d4l 4 місяці тому

    Nice video Arjan! First time I heard about this library and definitely I am going to use it in my projects.
    It would be great if you can record another video explaining how to override the dependencies for the tests.

  • @igor28lg
    @igor28lg 6 місяців тому +2

    Would love a video about your ideas on how to write good, testable Python code for AWS Lambda functions!!
    Also, hear your thoughts about AWS Lambda Powertools!
    BTW, your content is great, and I’ve learned so much from it! Keep being awesome!! 🤘🏻

    • @ArjanCodes
      @ArjanCodes  6 місяців тому +2

      Thanks for the suggestions Igor - and glad to hear the content is helpful 😎

  • @MichelAlbert
    @MichelAlbert 6 місяців тому +7

    Well thank you for reading our minds over distance 😆. We had a discussion on *exactly* this topic two days ago at the office and FastAPI came up as it does DI so cleanly. We were wondering whether there was a DI library for Python and "inject" definitely does fit that bill. *However*, I don't particularly like the way it handles the configuration/setup of the dependencies. FastAPI is much more intuitive. I'm really tempted to dig into the FastAPI implementation. Especially to see how it handles concurrency considering that "Depends(...)" is called during import-time. I have a good guess what's happening but I definitely want to dig in. It would be nice to have FastAPI style DI in non-web projects.
    We do DI a lot in our team but do it all manually. And it's really tough to strike a good balance between something that's not overly verbose but also explicit enough to stay maintainable.
    Thank you for this video and the shoutout to "inject". I will have a look and give it a trial run to see how it "feels". Even though my initial instinct is that it looks "too magical" I might get positively surprised.

    • @Antash_
      @Antash_ 6 місяців тому +1

      It best to stay away from DI frameworks that require you to change your code with Python's decorators, descriptors etc, there are exceptions to this rule, but if almost every class needs to be adjusted in such way then it introduces sooo much coupling to a single library, and DI is actually a pattern that should lower coupling ^^ Not to mention that such design uses global state underneath.

    • @MichelAlbert
      @MichelAlbert 6 місяців тому +1

      @@Antash_That gels with my personal feelings. I love my code to be explicit. And so far all DI frameworks I've seen introduce way too much magic. And - as you rightly say - tightly couple the code with that framework. Take Spring from the Java world for example. It's hard to get Spring out of your system once it's in there.

    • @Antash_
      @Antash_ 6 місяців тому

      2 that I know and used (a lot) are ets-labs dependency injector & proofit404 dependencies@@MichelAlbert I actually use them both at the same time, but on different levels of the architecture (it's a huuuge project). Both have their cons too though.

    • @shlomokallner3180
      @shlomokallner3180 3 місяці тому

      See `fast_depends` on PyPI. It might solve your problem.

  • @shadowviper95
    @shadowviper95 6 місяців тому +9

    I may just never have had a complex enough use case, but the frameworks just feel like using fancy global. I prefer the explicit nature of passing the args

    • @michaelhackman3195
      @michaelhackman3195 6 місяців тому +6

      Agreed - I want to see the data flow. Inject seems to make functions that take no arguments, and you instead have to hunt down where the objects are created instead of it just being right in front of you

    • @DrGreenGiant
      @DrGreenGiant 6 місяців тому +3

      Inject looks a lot like a global functools partial to me.
      My big hang up with fastapi is the heavy use of globals in the documentation. You really have to dig and experiment to figure out how to have an API that is composed across many different decoupled files (e.g. using Routers.)

  • @Golgafrincham
    @Golgafrincham 6 місяців тому

    Very interesting! It looked a bit cumbersome at first, but when you start to think about it it makes a lot of sense. I have this project at work where we have one Backend system with a extensive API. I'm looking into making Python bindings to this API and defining one function to perform the query or provide a client (for example httpx) would make it so much easier to test and make the code way cleaner.
    Never heard about "inject" before, thanks Arjan! :)

  • @MrTrebor2
    @MrTrebor2 Місяць тому

    It is said, that it is easy to write decoupled code (or pure functions). Hard part is to glue it together.
    I use DI manly for testing so db_arg = None, and inside db_arg = db_arg or get_db() is fine.

  • @StarLord1996
    @StarLord1996 6 місяців тому

    Dependency Injection can also be easily achieved using partial functions. As Arjan said, you create a "middleware" of sorts where in which you create a partial that provides the dependencies to your function. Let's say you've got a use case (in the form of a function, I see no need to create it as a class in Python), and this usecase depends on some databases interfaces. In the middleware file you provide the implementation to said interface to the usecase partial, and you call the partial building function from your endpoint. In this way you also comply with a clean code architecture, as you depend on abstractions and you don't worry about implementations in your business logic.

  • @andrewlane50
    @andrewlane50 6 місяців тому

    Hi Arjan! I'm curious about your mention of logging as a DI use-case. My understanding is that it should never be necessary to pass around `logger` objects because they are singletons and can always be returned with `getLogger()`, even if they've been defined in some outer scope (though I guess this assumes that you **always** want to use one already-known, particular logger inside those functions).

    • @jindy94
      @jindy94 5 місяців тому

      Hi Andrew, I have some experience in passing around logger objects in my FastAPI applications' internal functions! One use case is that you want to maintain information of each API calls made to the application in your logger prefixes or header information and having a singleton would mean that instead of passing down the logger object, you pass down the API information to be instantiated with the logger - which is similar amount of work: (e.g. `logger.info(..., extra=api_info)` instead of `logger = CustomLogger(api_info) ... logger.info(...)`). I would think this would be a valid use case right?

  • @matheusaugustodasilvasanto3171
    @matheusaugustodasilvasanto3171 4 місяці тому

    About logging, what are your concerns about creating logger objects at the top level of a module, and using that logger at the available scope?

  • @josefcernik362
    @josefcernik362 6 місяців тому +1

    Dependency injection is very good mechanism for MVC or MVP pattern, where you can separate conponents.

    • @JayDee28
      @JayDee28 6 місяців тому

      that’s exactly the application where I use it the most.

  • @Tsakaman
    @Tsakaman 6 місяців тому +1

    As a beginner, I have a question. In the example at the beginning of the video, who not have do something as a class instance method. Isn't that effectively the same?

  • @rockNbrain
    @rockNbrain 6 місяців тому +1

    Nice job Arjan, great video ... Nest.js and Angular both have this DIP paradigm in its core, and it's great!! tks a lot

  • @andrii-marynets
    @andrii-marynets 2 місяці тому +1

    Correct me if I am wrong, but doesn't it violate the rule explicit better than implicit? You decorate your function with @inject and then may call the function without any arg, which will do something for you.

  • @DagarCoH
    @DagarCoH 6 місяців тому

    I use dependency injection for sensor/actor access. If the device is present, it is used, if not, a mock device is used instead, allowing the program to run independent of hardware presence.

  • @ravenecho2410
    @ravenecho2410 6 місяців тому

    Dependency injection framework looks cool, seems like ur partializing like a set of functions with related objects - also seems like this could be a class.
    Im a bit of a fan of the more functional style - so this seems cool, imho

  • @buchi8449
    @buchi8449 6 місяців тому +3

    A bigger FastAPI app usually has a bigger main function for manually constructing the app by calling include_router, add_midddleware, etc.
    On the other hand, the main function of a Java Web application using SpringBoot is usually a single line calling "run()" with an application class regardless of the application size.
    Why? Because SpringBoot applications use Spring DI framework to construct the applications themselves (a router is a dependency of an app, for example). As this video explains, generic DI frameworks can manage the lifecycle of objects. Generic DI frameworks in Java, like Spring, can also apply IoC to the entire application lifecycle.
    Which one is better? I use both Python and Java in my work, but I prefer SpringBoot's approach, particularly for big applications.
    One of the points frequently missed about DI in Java is "convention over configuration (CoC)". DI frameworks in Java encourage programmers to use CoC to minimize boilerplate configuring DI. This has a good effect on maintaining code consistency in a big project.

  • @TangoFoxtrotWhiskey
    @TangoFoxtrotWhiskey 6 місяців тому +1

    I prefer dependency injection. For everything outside FastAPI I've just written my own decorators, used Pytest, or used the factory as a default parameter and call it in the function.
    The decorator needs to have a way to override the injection, I didn't see if Inspect has that ability.

  • @pythonlibrarian224
    @pythonlibrarian224 5 місяців тому

    I also tend to reach for constructor dependency injection (by pattern, not by library or framework), which works great until the root main function has dozens of parameters and all the classes have some subset of those parameters. Then it stops being easy. My main gripe with dep injection frameworks is the docs often try to hide how they work, making it seem like too much black magic and I don't need black magic in my code. (e.g. where is it constructed, when, how? someone is making those decisions, just in a different code file)

  • @hendrikmartina3552
    @hendrikmartina3552 6 місяців тому +1

    You should try kink dependency injection library. It is very simple and I would say that it is more pythonic

  • @sournk
    @sournk 6 місяців тому

    DI just makes code more complex and unobvious. Approaches like this make frameworks a set of magic. "Explicit is better than implicit" they said. Lately I've seen an increasing tendency to switch to Go in part because of these stuff in Python.

  • @maksimluzin1121
    @maksimluzin1121 6 місяців тому +2

    I just wanted to ask: How Dependency Injection is similar/different to the classical Strategy design pattern? Personally, I don't see (right now) huge differences between the two ideas, but maybe _you_ can tell me?

    • @Antash_
      @Antash_ 6 місяців тому +1

      DI is a broader concept, lots of OOP (and not only) patterns use DI

    • @ArjanCodes
      @ArjanCodes  6 місяців тому +1

      What @Antash696 writes. For example you could inject a function into another function. This is still dependency injection, but different from the Strategy pattern.

    • @maksimluzin1121
      @maksimluzin1121 6 місяців тому

      @@ArjanCodes , well, the idea is almost the same: you've to pass a Strategy object to a constructor to use different strategies through a parent/abstract class interface methods... Can you pass more examples for the DI usage in Python, pls?

  • @Nalewkarz
    @Nalewkarz 6 місяців тому +1

    Lagom is nice, Pythonic dependency injection library.

  • @RaveYoda
    @RaveYoda 6 місяців тому

    For logging I use Python builtins and just expose a logger object that's made singleton through an inheritance of a Singleton class that I made which has a __new__ method that does an increment check. I don't need to import anywhere and it's usable everywhere.

  • @JoãoMenezes-u3q
    @JoãoMenezes-u3q 6 місяців тому +1

    When there are deeply nested dependencies, DI frameworks can be a saver. However, I don't like the way inject does it with decorators. I prefer less intrusive ways like punq, which would be easier to replace and gives me enough control. I feel that the dependency injector framework has too much boilerplate and makes the code confuse. I also tried the dependencies framework, however, it caused some problems, since you get an instance by requesting a class, and it seems that it is no longer maintained.

    • @Antash_
      @Antash_ 6 місяців тому

      `dependencies` has less boilerplate than `dependency injector` but in a large project when you need a lot of "scopeing" the boilerplate will blast in size. In general I dont see a good, maintained DI framework that ticks all my boxes in Python right now ;(

  • @jjenn050
    @jjenn050 5 місяців тому

    Why is yield used in get_db? I see how it can help dealing with exceptions within the correct context, but what makes the finally block execute?

  • @remcogreve7982
    @remcogreve7982 6 місяців тому

    There is a book called "A philosophy of software design" by John Ousterhout that really made me think about things like this. He is talking about deep and shallow modules. I think that a dependency injection framework would fall in the category of a shallow module and should be avoided.

    • @MikeCorvin-x4p
      @MikeCorvin-x4p 6 місяців тому

      That was my spidey-sense as well. Agree with the other comments that I prefer the injection to be explicit where it happens so it's apparent when one is reading just that chunk of code. But... would have to test drive this shiny 'new' (to me) toy to see if there are use cases for it that work for me. (dang-it, Arjan, you keep turning us on to new toys and I can't keep up with trying them out!)

  • @hello_world_zz
    @hello_world_zz 3 місяці тому

    Thanks Arjan

  • @coryphoenixxx8238
    @coryphoenixxx8238 3 місяці тому +1

    What's about "dishka"?

  • @loek1455
    @loek1455 6 місяців тому

    I remember having some trouble recently, because they changed when the finally block is executed. Basically I yielded a path of a folder and unlinked it in the finally block. The change was, that the finally block was executed before the response. This resulted in the file i wanted to return was getting deleted before the FileResponse could return :D Took me some time to find that^^

  • @alexf8866
    @alexf8866 6 місяців тому +1

    I missed a word of caution to DI together with demos. DI is a heavy weight 3rd party dependency. How does the error message looks if you have two or more injection candidates? Then you have to give names to your injected instance or something like this. Like if you need a second sqlite3.Connection in the example. What if everybody uses it and already a sqlite3.Connection comes with a 3rd party library. Then you have to search these builder methods in some 3rd party dependency or do you do then some monkey patching? Only showing the happy path looks smooth but do you poison your code with this?

    • @Lexaire
      @Lexaire 6 місяців тому

      Yea I really wish examples used Postgres connection pools instead of SQLite so it were closer to real world usage.

  • @naveenkumardongre
    @naveenkumardongre 6 місяців тому

    so nicely explained thank u so much🤝

    • @ArjanCodes
      @ArjanCodes  6 місяців тому

      I'm glad you enjoyed it!

  • @franco-gil
    @franco-gil 6 місяців тому

    [7:25] how do we test the `bind()` method if we have to?
    i use Python as my main language (projects and work) but i am in love with the DI from PHP / Java, i am not totallty sure how this gonna work with Python in the long run.

  • @Kitsune_Dev
    @Kitsune_Dev 6 місяців тому

    I can’t use DI with Luau because it disables optimizations 😢

  • @plato4ek
    @plato4ek 6 місяців тому

    10:46 Why is there only `yield db` inside the try block? I guess this cannot raise an exception in any circumstances. Shouldn't the `db = SessionLocal()` also be inside the try block?

    • @Lexaire
      @Lexaire 6 місяців тому +1

      It's correct. The try-except is run on the outside of the calling function, so that if your function that uses get_db() throws an error then the db connection still gets closed. The flow goes: FastAPI calls your get_db dependency, then yield db to your route function. Then if your route function throws an error, the finally: clause is called and the connection is closed. And if your function doesn't throw an error, the finally clause is still called and it closes the connection.

  • @_DRMR_
    @_DRMR_ 6 місяців тому

    I wonder if `inject` can help deal with mocking dependencies where in production you use an async function but in testing everything is synchronous. Lately I've had to deal with type checking in our code and had to explicitly ignore some function signatures because the type definitions created conflicts. So off-loading these dependencies to a library like inject maybe can make this a bit cleaner and not have the static analysis trip over such details.

    • @scotter7663
      @scotter7663 6 місяців тому

      I'm not much of a python developer but this pattern is used frequently in Java. In java you can inject an ExecutorService which is an abstraction around a thread pool and in production code use an actual thread pool then in tests use a "direct" executor that executes logic inline.
      I'm not sure if a similar pattern would apply in Python. For languages that use async/ await (or similar) keywords I could see how this would be more difficult to implement

  • @ramimashalfontenla1312
    @ramimashalfontenla1312 6 місяців тому

    Super useful!

  • @evgeny6692
    @evgeny6692 6 місяців тому

    Hey Arjan very nice video as always, thank you very much! I have a question about Depends in FastAPI. Is it so that the DI works only with route handler or would it be possible to add some abstraction layer, let's say a repository and have the db injected there? Or is it only going to possible by passing it from the route handler into the repo via constructor?

    • @shlomokallner3180
      @shlomokallner3180 3 місяці тому

      try using `fast_depends`. Note: I am NOT the creator of that library. I just used it once for a project with mixed results.

  • @MyYouTubeAccount17
    @MyYouTubeAccount17 6 місяців тому

    Why haven't y'all been using the Spring Java Framework for the last 20 years?

  • @ravenecho2410
    @ravenecho2410 6 місяців тому

    For pyrhon ive found, load_dotenv and theres this override package as well, and a pattern thats very similar to dataclass but is "baseclass" super interesting. Im also starting to see non-standard usage of init files... in certain designs if the folder are all implementations then __init__ is the base class.
    Working on deploying some open source, extending it, as well as contributing back bug fixes.
    I also had this weird thought that modules (a file is just a class), and like closures are just calling modules with variables defined at call (the state) - which is equivalent to their oop defn for class instances(?). And ive always implemented singletons as just only class methods... not sure if there is a betrerway (and have no init), those just feel like a module that has all variable refrences in the module itself! 😅
    But meh! Just a thought how closures, classes, modules etc are just all an implementation of abstraction 🙂

    • @Lexaire
      @Lexaire 6 місяців тому

      Try out pydantic-settings! It handles configurations elegantly.

  • @praganesha.six-a8865
    @praganesha.six-a8865 6 місяців тому

    Anyone say whay font and theme was Arjan using.It looks cool..

  • @Alexr26
    @Alexr26 6 місяців тому

    Hey Arjan! Thank you for your content.
    You can integrate FastAPI with PyNest, and inject PyNest's service layer into the FastAPI controllers.
    It would be great if you can do a video about PyNest and FastAPI integration.

  • @EW-mb1ih
    @EW-mb1ih 5 місяців тому

    I would not use inject because:
    + I don't see the point to depend on a third party library for this kind of work,
    + I like to use dependency injection in a simple yet comprehensive way

  • @bigutubefan2738
    @bigutubefan2738 6 місяців тому +3

    DI Frameworks in Python are an unreadable mess of buggy 'magic', that just encourage people to write code that is not only broken without the framework, but butt ugly.
    Want to call a function without specifying dependencies? Just give its args default values FFS (if you want them lazy loaded, default values of None, then arg = arg or ExpensiveResource()).

  • @cameronmcnz
    @cameronmcnz 8 днів тому

    So let me get this straight, any time you pass an argument to a method you are doing dependency injection?
    So basically every line of code ever written that passes parameters is doing DI? That doesn't make any sense.
    By extrapolation, that would then mean any code that that calls a parameterized method is actually an inversion of control container?
    HashMap map = new HashMap(10);
    So that's dependency injection, and this comment is now an open source DI and IoC container?
    Dependency Injection - I don't think that word means what you think it means.
    🤦‍♂

  • @고기집아들
    @고기집아들 6 місяців тому

    Wow what really needs

  • @franktewierikholscher
    @franktewierikholscher 27 днів тому

    Why don't you ever pronounce the "i" in the word "repository". You always say "repostory"?

  • @arpitkumar4525
    @arpitkumar4525 5 місяців тому

    Be careful guys, DI frameworks will make your code unreadable and even harder to debug for a person new to your codebase. Just self made IoC can be considered.

  • @Michallote
    @Michallote 6 місяців тому

    I don't think I will use this in the near future to be honest. Seems too much of a boilerplate

  • @shahaffrsshahaffrs5190
    @shahaffrsshahaffrs5190 6 місяців тому

    Such a killer joke Quite Laughable (SQL) 😂 be careful this injection won't kill your database.

  • @garrywreck4291
    @garrywreck4291 6 місяців тому +4

    Implicit garbage that only complicates the code