stevan / BreadBoard

Inversion of Control and Dependency Injection for Perl
26 stars 24 forks source link

RFC: Add a service injector for service "currying" #30

Open zostay opened 11 years ago

zostay commented 11 years ago

I have a project where it would be nice to have a set of services that act as a sort of template for another set of services. For example, I could define a generic database connector service that might be used to help construct a whole bunch of real database connectors. I was contemplating the benefits of doing this through Bread::Board like this:

container ResourceTypes => as {
  container DB => as {
    service connection => (
      class => 'MyApp::DB::Handle',
      parameters => {
        host => { isa => 'Str' },
        port => { isa => 'Int' },
        schema => { isa => 'Str' },
        user => { isa => 'Str' },
        password => { isa => 'Str' },
    },
  };
};

Later, I define a set of actual configurations which can be loaded something like this:

container Resources => [ 'ResourceType' ] => as {
  service devdb_host => 'devdb';
  service devdb_port => 3306;
  service devdb_schema => 'myapp';
  service devdb_user => 'sandbox';
  service devdb_password => 'secret';

  service devdb => (
    service_type => 'Service',
    service_path => 'ResourceType/DB/connection',
    dependencies => {
      host => depends_on('devdb_host'),
      port => depends_on('devdb_port'),
      schema => depends_on('devdb_schema'),
      user => depends_on('devdb_user'),
      password => depends_on('devdb_password'),
    },
  );
};

The ServiceInjection class would simply do a fetch() on the named service_path and pass through the parameters setup by the dependencies and parameters, allowing one service to be built up from another.

This works as a sort of currying, I guess, allowing one service to be built upon another.

Thoughts? If that sounds like a reasonable idea, I can send a pull request. Otherwise, I may just find a different way because I don't really need to create this just for me.

stevan commented 11 years ago

Maybe I am missing something, but how is this not the same as the parameterized containers?

zostay commented 11 years ago

I'd say it's pretty similar to parameterized containers, but parameterized containers hit the problem from the opposite direction. I may even be able to get it to work that way, but it could end up being a big convoluted. The main issue is that I want to use one container to contain the Bread::Board to pass into a job, which might contain several database handles each with their own configuration. All the ways I can think of to make a parameterized container work are more complicated than just currying the services. However, it's quite possible that my understanding of parameterized containers is naive.

What I'm proposing is more like service aliases that modify the service being aliased. Without adding a ServiceInjection class, what I'm proposing could be accomplished via a block injector like this (though, this is not tested, so I might have gotten the details wrong):

        service devdb => (
            block => sub {
                my $s = shift;
                $s->fetch('/ResourceTypes/DB/connection')->get(%{ $s->params });
            },
            dependencies => {
                host => depends_on('devdb_host'),
                port => depends_on('devdb_port'),
                schema => depends_on('devdb_schema'),
                user => depends_on('devdb_user'),
                password => depends_on('devdb_password'),
            },
        );