Should You Use Dependency Injection Frameworks?

Поділитися
Вставка
  • Опубліковано 26 чер 2024
  • Dependency injection separates resource creation from usage, decoupling your code for better management. In this video, we dive into the next level: dependency injection frameworks. Discover what they do and why they're essential for streamlined coding.
    👷 Join the FREE Code Diagnosis Workshop to help you review code more effectively using my 3-Factor Diagnosis Framework: www.arjancodes.com/diagnosis
    🔥 GitHub Repository: git.arjan.codes/2024/dependen...
    💻 ArjanCodes Blog: www.arjancodes.com/blog
    ✍🏻 Take a quiz on this topic: www.learntail.com/quiz/xzirtx
    Try Learntail for FREE ➡️ www.learntail.com/
    🎓 Courses:
    The Software Designer Mindset: www.arjancodes.com/mindset
    The Software Architect Mindset: Pre-register now! www.arjancodes.com/architect
    Next Level Python: Become a Python Expert: www.arjancodes.com/next-level...
    The 30-Day Design Challenge: www.arjancodes.com/30ddc
    🛒 GEAR & RECOMMENDED BOOKS: kit.co/arjancodes.
    👍 If you enjoyed this content, give this video a like. If you want to watch more of my upcoming videos, consider subscribing to my channel!
    Social channels:
    💬 Discord: discord.arjan.codes
    🐦Twitter: / arjancodes
    🌍LinkedIn: / arjancodes
    🕵Facebook: / arjancodes
    📱Instagram: / arjancodes
    ♪ Tiktok: / arjancodes
    👀 Code reviewers:
    - Yoriz
    - Ryan Laursen
    - Dale Hagglund
    - Kit Hygh
    - Alexander Milden
    - Bean
    🎥 Video edited by Mark Bacskai: / bacskaimark
    🔖 Chapters:
    0:00 Intro
    0:49 What is dependency injection?
    5:34 'inject' Library: Advanced Features
    9:53 FastAPI: Elegant and Intuitive DI
    12:19 Detailed Comparison and Use Cases
    14:15 Outro
    #arjancodes #softwaredesign #python
    DISCLAIMER - The links in this description might be affiliate links. If you purchase a product or service through one of those links, I may receive a small commission. There is no additional charge to you. Thanks for supporting my channel so I can continue to provide you with free content each week!

КОМЕНТАРІ • 99

  • @ArjanCodes
    @ArjanCodes  3 місяці тому +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 3 місяці тому

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

    • @shlomokallner3180
      @shlomokallner3180 22 дні тому

      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 3 місяці тому +16

    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 3 місяці тому +1

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

  • @kayakMike1000
    @kayakMike1000 3 місяці тому +72

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

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

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

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

      y made my day 😂

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

      ah!

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

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

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

      I (dependen)see what you did there.

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

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

  • @jarzez
    @jarzez 3 місяці тому +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 Місяць тому +1

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

  • @paxdriver
    @paxdriver 3 місяці тому +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.

  • @slavoie
    @slavoie 3 місяці тому +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!

  • @user-xw1jc1wy5t
    @user-xw1jc1wy5t Місяць тому

    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.

  • @shadowviper95
    @shadowviper95 3 місяці тому +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 3 місяці тому +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 3 місяці тому +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.)

  • @UNgineering
    @UNgineering 3 місяці тому +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 3 місяці тому +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 3 місяці тому

      @@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 2 місяці тому +1

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

  • @rockNbrain
    @rockNbrain 3 місяці тому +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

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

    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! :)

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

    Super useful!

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

    so nicely explained thank u so much🤝

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

      I'm glad you enjoyed it!

  • @igor28lg
    @igor28lg 3 місяці тому +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  3 місяці тому +2

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

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

    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

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

    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?

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

    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.

  • @MichelAlbert
    @MichelAlbert 3 місяці тому +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_ 3 місяці тому +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 3 місяці тому +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_ 3 місяці тому

      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 22 дні тому

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

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

    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.

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

    Thanks Arjan

  • @timelschner8451
    @timelschner8451 3 місяці тому +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 3 місяці тому

      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?

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

    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 3 місяці тому

      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?

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

    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.

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

    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.

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

    Lagom is nice, Pythonic dependency injection library.

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

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

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

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

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

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

  • @pythonlibrarian224
    @pythonlibrarian224 2 місяці тому

    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)

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

    [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.

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

    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 22 дні тому

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

  • @TangoFoxtrotWhiskey
    @TangoFoxtrotWhiskey 3 місяці тому +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.

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

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

  • @jjenn050
    @jjenn050 2 місяці тому

    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?

  • @coryphoenixxx8238
    @coryphoenixxx8238 23 дні тому +1

    What's about "dishka"?

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

    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?

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

    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 3 місяці тому

      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

  • @alexf8866
    @alexf8866 3 місяці тому +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 3 місяці тому

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

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

    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.

    • @user-lz3wv6sk5k
      @user-lz3wv6sk5k 3 місяці тому

      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!)

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

    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 3 місяці тому +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.

  • @maksimluzin1121
    @maksimluzin1121 3 місяці тому +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_ 3 місяці тому +1

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

    • @ArjanCodes
      @ArjanCodes  3 місяці тому +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 3 місяці тому

      @@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?

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

    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.

  • @user-eb9nv7tr8w
    @user-eb9nv7tr8w 3 місяці тому

    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_ 3 місяці тому

      `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 ;(

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

    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 3 місяці тому

      Try out pydantic-settings! It handles configurations elegantly.

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

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

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

    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^^

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

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

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

    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 2 місяці тому

    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

  • @user-mk2lq1st3y
    @user-mk2lq1st3y 3 місяці тому

    Wow what really needs

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

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

  • @arpitkumar4525
    @arpitkumar4525 2 місяці тому

    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.

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

    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()).

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

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

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

    Implicit garbage that only complicates the code