Tuesday, March 06, 2012

Why Bread::Board looks mostly redundant

This is based on two assumptions - that you don't use BB as a kind of Service Locator (but I agree with for example Dependency Injection != using a DI container that this is an anti-pattern) and what 'mostly follows' that the product of your BB container is just one object - the application class. I believe these are good guidelines for software architecture. With those two assumptions all that BB gives you is that you can name your partial results and then use them in later computations, but Perl has a good support for this - it is called variables.

For example let's take the original example from Bread::Board synopsis: Now - let's do the same with just variables: You can also feel fancy and do it with Moose lazy attributes: This is not longer than the BB example and it uses generic tools.

9 comments:

Caleb Cushing said...

but if I say have a bigger application, and on the application level I have an accessor that it the DI I can do ->bb->resolve ...

with yours I then have to put the individual variables as lazy constructors in an object, as you later suggest.

Essentially as I can tell it, BB is not for small things.

zby said...

Where do you want to do ->bb->resolve? After the main object is already built all stuff should be available via it's attributes. If you pass the BB container around then you are following the Service Locator anti-pattern. I have to admit I have not yet tried BB with very bit applications - but I don't see how having a lot of things to instantiate would change the situation.

Caleb Cushing said...

I was considering using it to construct my Model instances which also had dependencies on database and log object in a Mojo app (which realistically speaking had poor support for such things). Project is cancelled so I can't tell you how it would have turned out, but before that I was using a factory, and a seperate subroutine for each in the main class, which seems ultimately messier.

Stevan Little said...

So you are forgetting a number of things.

First, if you use variables you have to care about and be very careful with order of operations. Adding in a new dependency or component will cause you to have to review your entire chain of events. If you have circular dependencies, you are basically screwed. And of course, Bread::Board will handle all this for you.

Second, you have no easy way to control lifecycle, you will end up needing to do this manually. Right now, in your variables example, everything would use the same DBI connection, if for some reason you later needed each subcomponent to use it's own DBI connection, you would have a lot of code refactoring to do. Doing this with Bread::Board would require a one line change.

And lastly, this won't scale. What if this component suddenly becomes just another component in a larger system? Can you easily share resources between them? No, because everything will be doing all it's own initialization.

We are currently using Bread::Board at work to power a large system. It has a number of independent data modules, the top level component of which isa(Bread::Board::Container), which themselves have multiple Model objects within them (created and managed by the Bread::Board::Container). Each module is treated as a wholly independent component for testing purposes, but when put into production, they are merged together under a top-level component (itself also a Bread::Board::Container). Sharing resource such as DB connections, ElasticSearch handles, Logging information, etc., all that is handled via Bread::Board and just works in whichever environment we happen to be using it in. Additionally we take advantage of the Bread::Board configuration itself to actually provide meta-information to the components so they know what other components are available.

Basically, you are thinking too small. The Bread::Board synopsis example is for illustrative purposes only, it really works much better in larger situations.

zby said...

I did not have a chance to use BB on a really big project - so I have no ammunition in this discourse. But I'll not remove this article :)

There probably is more to BB then I see now - but I still have the feeling that perhaps this additional functionality are patches of features that don't really require to be coupled together into one big bundle. For example - the life cycle that you mention with simple variables by default you'd get a Singleton life cycle - true, but if you planned from the beginning that you'd might change it in the future - then you could add one additional redirection and have the same room for change as you have with BB. For example you could use functions or methods instead of variables.

zby said...

To be more concrete - with Moose lazy attributes you can have singleton lifecycle - but you can easily replace the attribute with it's builder - and you get the 'every time a new' lifecycle.

Stevan Little said...

zby, no need to remove it, I am simply responding to your post.

The idea of using lazy Moose attributes is not a good one, and not much in the spirit of DI. It means that you are tightly coupling the creation of other objects to your main object.

And while yes it is true, you could refactor your variable solution to make it do what I suggested, why write code when I already did? And trust me on this one, the larger your configuration, the more work that refactoring becomes, not to mention the inevitable bugs you will introduce.

As we have discussed many times before, Bread::Board is just a tool, it is one I find quite handy, no one says you have to use it ;)

zby said...

OK - I changed the title :)

But back to the subject - how about using lazy attributes in your Factory class (instead of your Application class)? DI requires that you use some Factory class, be it DI container like BB or - what I suggest here maybe a plain vanilla Moose class.

Anonymous said...

I'd choose simplicity over flexibility. And when I need flexibility I'd choose to refactor. But not before I need it. YAGNI.