5 ms·
How many active contributors and lines of code does your projects have? I’ve found that the advantages for DI frameworks (besides encouraging more unit tests a
by strulovich 7y ago
How many active contributors and lines of code does your projects have?
I’ve found that the advantages for DI frameworks (besides encouraging more unit tests and enabling them) to be more valuable for bigger and messier projects.
- sagichmal 7y agoI find precisely the opposite, that as a project gains (and loses, and gains again) maintainers, the indirection and mental overhead that DI frameworks necessarily bring to the table vastly outweigh whatever perceived benefit there was in introducing them. Conversely, a declarative, step-wise func main, with components manually constructed and passed to each other as dependencies where necessary, is sometimes tedious, but never confusing, and always refreshingly easy to maintain, no matter how much time you've spent with the project.
- tempguy9999 7y agoI've literally no idea what the second paragraph means but I suspect it's quite important. Could you point to an example of this being done? Thanks.
- twic 7y agoThis sounds like the main methods i write (in Java, also not using a DI framework). Like this: var database = new DatabaseFacade(config.get("db.host"), config.get("db.port", Integer::parseInt)); var users = new UserRepository(database); var catalogue = new ProductRepository(database); var cart = new ShoppingCartController(users, catalogue); It's imperative code, of course, but it's sort of declarative in that all the code is doing is wiring things up. It's step-wise in that it does one simple thing after another; it might be quite long, as there are a lot of components to create, but it can be understood locally. Components are manually constructed, by calling constructors, rather than via reflection done by the framework. Components are passed to each other as parameters to establish dependencies, rather than there being some sort of rule-driven lookup, as a framework would do. It is indeed pretty tedious to read, as it's just lots of constructor calls, with no thrilling action. But it's so simple it's never confusing (and you get to use the full power of the IDE to navigate it, jumping to definitions and uses etc). And, as such, easy to maintain. Definitely refreshingly so if you've come from Spring or Guice.
- qes 7y agoWould you consider this viable in a monolith, though? The primary application I work on has hundreds of classes that get dependencies injected through DI. Many can be and are singletons, but many - including some very commonly used ones - cannot. If I manually wired dependencies it would add thousands of lines of code, and there are some where if I changed the constructor parameters I would have to modify 1000+ call sites. And this isn't even a particularly massive or complex application.
- twic 7y agoThe largest application where we use this pattern has 131737 lines of code (according to cloc), and four 'main' classes which do dependency injection, which create roughly 155, 145, 111, and 95 components. Those files total 8949 lines of code. They're doing more than just injection, though, there's quite a bit of code for other kinds of setup and configuration. Remember that this setup code is just code, so if you find that adding a constructor parameter means modifying a thousand call sites, you should probably refactor it a bit first, so that it doesn't. That being said, this approach to DI does take some tedious manual work to maintain. But by paying that cost, you get to take a magical DI framework out of the picture entirely.
- dvlsg 7y agoNot OP, but if you're looking for something to search for, I suspect they're describing "Pure DI" or using a "Composition Root". https://blog.ploeh.dk/2014/06/10/pure-di/ https://blog.ploeh.dk/2014/06/10/pure-di/ https://stackoverflow.com/questions/6277771/what-is-a-composition-root-in-the-context-of-dependency-injection https://stackoverflow.com/questions/6277771/what-is-a-compos...
- sagichmal 7y agoOh, sorry. twic basically got what I meant. I'd expand the example a bit to demonstrate how to use different implementations of things: var db IDatabase { if dev_mode db = new InMemDatabase() else db = new PostgresDatabase(dsn) } var users = new UsersRepository(db) var catalogue = new ProductRepository(db) // ... var cart = new ShoppingCartController(users, catalogue, logger, metrics, ...)
- dvlsg 7y agoI've found containers are helpful when you want to replace one or two specific pieces in the middle of the dependency tree for testing - let's say a mock database, for example. On the other hand, I've also found myself avoiding writing code which requires deep mocks like that in order to test. Functional core, imperative shell helps with that.