Provided by: libbread-board-perl_0.32-1_all bug

NAME

       Bread::Board::Manual::Concepts - An overview of the concepts in Bread::Board

VERSION

       version 0.32

INTRODUCTION

       This document attempts to convey the central concepts of Bread::Board and show how they
       work together to manage both object lifecycles and object dependencies.

       In this document we use the raw OO syntax of Bread::Board, this is so that the concepts
       being illustrated are not clouded by syntactic sugar. We only introduce the sugar layer at
       the end, at which point we hope that it will become clear what is going on "under the
       hood" when you use it.

CONCEPTS

   What is Inversion of Control?
       Inversion of Control (or IoC) is the very simple idea of releasing control of some part of
       your application over to some other part of your application, be it your code or an
       outside framework.

       IoC is a common paradigm in GUI frameworks, whereby you give up control of your
       application flow to the framework and install your code at callbacks hooks within the
       framework. For example, take a very simple command line interface; the application asks a
       question, the user responds, the application processes the answer and asks another
       question, and so on until it is done. Now consider the GUI approach for the same
       application; the application displays a screen and goes into an event loop, users actions
       are processed with event handlers and callback functions. The GUI framework has inverted
       the control of the application flow and relieved your code from having to deal with it.

       IoC is also sometimes referred to as 'Dependency Injection' or the 'Dependency Injection
       Principle', and many people confused the two.  However IoC and dependency injection are
       not the same, in fact the concepts behind dependency injection are actually just an
       example of IoC principles in action, in particular about your applications dependency
       relationships. IoC is also sometimes referred to as the Hollywood Principle because of the
       don't call us we'll call you approach of things like callback functions and event
       handlers.

       Howard Lewis Ship, the creator of the HiveMind IoC Framework, once referred to dependency
       injection as being the inverse of garbage collection. With garbage collection you hand
       over the details of the destruction of your objects to the garbage collector. With
       dependency injection you are handing over control of object creation, which also includes
       the satisfaction of your dependency relationships.

       The following sections will explain the basis concepts around the Bread::Board and how it
       relates to the concept of IoC.

   Containers
       The central part of just about any IoC framework is the container.  A container's
       responsibilities are roughly to dispense services and to handle the resolution of said
       service's dependency relationships.

       First we can start with a simple container for our services to live in. We give the
       container a name so that we can address it later on, think of this like a package
       namespace.

         my $c = Bread::Board::Container->new( name => 'Application' );

       Next we need to add a service to that container (we will explain services a little later
       on).

         $c->add_service(
             Bread::Board::BlockInjection->new(
                 name  => 'logger',
                 block => sub { Logger->new() }
             )
         );

       Now if we want an instance of our 'logger' service, we simply ask the container for it.

         my $logger_service = $c->fetch('logger');

       And we then can ask the service to give us an instance of our Logger object.

         my $logger = $logger_service->get;

       Or if we want to make this even simpler we can use the "resolve" method of the container
       object.

         my $logger = $c->resolve( service => 'logger' );

       The "resolve" method will look up the service asked for and return the instance, which is
       basically equivalent to the chained "fetch" and "get" calls above.

   Dependency Management
       Dependency management is also quite simple, and is easily shown with an example. But first
       lets create another component for our container, a database connection.

         $c->add_service(
             Bread::Board::BlockInjection->new(
                 name  => 'db_conn',
                 block => sub { DBI->connect('dbi:mysql:test', '', '') }
             )
         );

       Now lets add an authenticator to our container. The authenticator requires both a database
       connection and a logger instance in its constructor. We specify dependency relationships
       between services by providing a HASH of Bread::Board::Dependency objects which themselves
       have a path to the services they depend upon. In this case since all these services are in
       the same container, the service path is simply the name.

         $c->add_service(
             Bread::Board::BlockInjection->new(
                 name  => 'authenticator',
                 block => sub {
                       my $service = shift;
                       Authenticator->new(
                           db_conn => $service->param('db_conn'),
                           logger  => $service->param('logger')
                       );
                 },
                 dependencies => {
                     db_conn => Bread::Board::Dependency->new(
                         service_path => 'db_conn'
                     ),
                     logger  => Bread::Board::Dependency->new(
                         service_path => 'logger'
                     ),
                 }
             )
         );

       As you can see, the first argument to our service subroutine is actually our service
       instance. Through this we can access the resolved dependencies and use them in our
       Authenticator object's constructor.

       The above example is deceptively simple, but really powerful.  What you don't see on the
       surface is that Bread::Board is completely managing initialization order for you. No
       longer to do you need to worry if your database is connected or your logger initialized
       and in what order you need to do that initialization, Bread::Board handles that all for
       you, including circular dependencies. This may not seem terribly interesting in such a
       small example, but the larger an application grows, the more sensitive it becomes to these
       kinds of initialization order issues.

   Lifecycle Management
       The default lifecycle for Bread::Board::Service components is a 'prototype' lifecycle,
       which means each time we ask for say, the logger, we will get a new instance back. There
       is also another option for lifecycle management that we call 'Singleton'. Here is an
       example of how we would use the 'Singleton' lifecycle to ensure that you always get back
       the same logger instance.

         $c->add_service(
             Bread::Board::BlockInjection->new(
                 lifecycle => 'Singleton',
                 name      => 'logger',
                 block     => sub { Logger->new() }
             )
         );

       Now each time we request a new logger component from our container we will get the exact
       same instance. Being able to change between the different lifecycles by simply changing
       one service parameter can come in very handy as you application grows. Extending this
       idea, it is possible to see how you could create your own custom service objects to manage
       your specific lifecycle needs, such as a pool of database connections.

   Services
       Up until now, we have shown the default way of creating a service by using the
       Bread::Board::BlockInjection and an anonymous subroutine. But this is not the only way to
       go about this. Those who have encountered IoC in the Java world may be familiar with the
       idea that there are 3 'types' of IoC/Dependency Injection; Constructor Injection, Setter
       Injection, and Interface Injection.  In Bread::Board we support both Constructor and
       Setter injection, it is the authors opinion though that Interface injection was not only
       too complex, but highly java specific and the concept did not adapt itself well to perl.

       Block Injection
           While not in the 'official' 3 types (mostly because it's not possible in Java), but
           found in a few Ruby IoC frameworks, BlockInjection is by far the most versatile type.
           It simply requires a subroutine and a name and you do all the rest of it yourself.

             $c->add_service(
                 Bread::Board::BlockInjection->new(
                     name  => 'logger',
                     class => 'ComplexLogger',
                     block => sub {
                         my $s = shift;
                         my $l = ComplexLogger->new(
                             file => $s->param('log_file')
                         );
                         $l->init_with_timezone( $s->param('timezone') );
                         $l->log_timestamp;
                         $l;
                     },
                     dependencies => {
                         log_file => Bread::Board::Dependency->new(
                             service_path => 'log_file'
                         ),
                         timezone => Bread::Board::Dependency->new(
                             service_path => 'timezone'
                         ),
                     }
                 )
             );

           BlockInjection comes in really handy when your object requires more then just
           constructor parameters and needs some more complex initialization code. As long as
           your subroutine block returns an object, everything else is fair game. Also note the
           optional 'class' parameter, which when supplied will perform a basic type check on the
           result of the subroutine block.

       Constructor Injection
           Bread::Board also supports Constructor Injection. With constructor injection, the
           service calls the class's constructor and feeds it the dependencies you specify. This
           promotes what is called a "Good Citizen" object, or an object who is completely
           initialized upon construction.

             $c->add_service(
                 Bread::Board::ConstructorInjection->new(
                     name         => 'authenticator',
                     class        => 'Authenticator',
                     dependencies => {
                         db_conn => Bread::Board::Dependency->new(
                             service_path => 'db_conn'
                         ),
                         logger  => Bread::Board::Dependency->new(
                             service_path => 'logger'
                         ),
                     }
                 )
             );

           Since Bread::Board is built both with Moose and for use with Moose objects, it makes
           the assumption here that the constructor takes named arguments. Here is our earlier
           authenticator service rewritten to use constructor injection. This is by far the
           simplest injection type as it requires little more then a class name and a HASH of
           dependencies.

       Setter Injection
           Bread::Board also supports Setter Injection. The idea behind setter injection is that
           for each component dependency a corresponding setter method must exist. This style has
           been popularized by the Spring java framework. I will be honest, I don't find this
           type of injection as useful as block or constructor, but it can come in handy if your
           object prefers you to call setters to initialize it. Here is a fairly contrived
           example using the JSON module.

             $c->add_service(
                 Bread::Board::SetterInjection->new(
                     name         => 'json',
                     class        => 'JSON',
                     dependencies => {
                         utf8   => Bread::Board::Literal->new(
                             name  => 'true',
                             value => 1
                         )
                         pretty => Bread::Board::Literal->new(
                             name  => 'true',
                             value => 1
                         )
                     }
                 )
             );

           Setter injection actually creates the object without passing any arguments to the
           constructor, then loops through the keys in the dependency HASH and treats each key as
           a method name, and each value as that method's argument. In this case, the above is
           the equivalent of doing:

              my $json = JSON->new;
              $json->utf8(1);
              $json->pretty(1);

           You might have been wondering about the fact we didn't specify
           Bread::Board::Dependency objects in our dependency HASH, but instead supplied
           Bread::Board::Literal instances. Bread::Board::Literal is just another Service type
           that simply holds a literal value, or a constant. When dependencies are specified like
           this, Bread::Board internally converts them into Bread::Board::Dependency whose
           service is already resolved to that service.

   Hierarchal Containers
       Up until now, we have seen basic containers which only have a single level of components.
       As your application grows larger it may become useful to have a more hierarchal approach
       to your containers.  Bread::Board::Container supports this behavior through its many
       subcontainer methods. Here is an example of how we might re-arrange the previous examples
       using subcontainers.

         my $app_c = Bread::Board::Container->new( name => 'app' );

         my $db_c = Bread::Board::Container->new( name => 'database' );
         $db_c->add_service(
             Bread::Board::BlockInjection->new(
                 name  => 'db_conn'
                 block => sub {
                     my $s = shift;
                     return DBI->connect(
                         $s->param('dsn'),
                         $s->param('username'),
                         $s->param('password')
                     );
                 },
                 dependencies => {
                     dsn      => Bread::Board::Literal->new(
                         name  => 'dsn',
                         value => 'dbi:mysql:test'
                     ),
                     username => Bread::Board::Literal->new(
                         name  => 'username',
                         value => 'user'
                     ),
                     password => Bread::Board::Literal->new(
                         name  => 'password',
                         value => '****'
                     ),
                 }
             )
         );

         $app_c->add_sub_container( $db_c );

         my $log_c = Bread::Board::Container->new( name => 'logging' );
         $log_c->add_service(
             Bread::Board::Literal->new(
                 name  => 'log_file',
                 value => '/var/log/app.log'
             )
         );
         $log_c->add_service(
             Bread::Board::ConstructorInjection->new(
                 name  => 'logger',
                 class => 'Logger',
                 dependencies => {
                     log_file => Bread::Board::Dependency->new(
                         service_path => 'log_file'
                     )
                 }
             )
         );

         $app_c->add_sub_container( $log_c );

         my $sec_c = Bread::Board::Container->new( name => 'security' );
         $sec_c->add_service(
             Bread::Board::ConstructorInjection->new(
                 name         => 'authenticator',
                 class        => 'Authenticator',
                 dependencies => {
                     db_conn => Bread::Board::Dependency->new(
                         service_path => '../database/db_conn'
                     ),
                     logger  => Bread::Board::Dependency->new(
                         service_path => '../logging/logger'
                     ),
                 }
             )
         );

         $app_c->add_sub_container( $sec_c );

         $app_c->add_service(
             Bread::Board::ConstructorInjection->new(
                 name         => 'app',
                 class        => 'Application',
                 dependencies => {
                     auth    => Bread::Board::Dependency->new(
                         service_path => '/security/authenticator'
                     ),
                     db_conn => Bread::Board::Dependency->new(
                         service_path => '/database/db_conn'
                     ),
                     logger  => Bread::Board::Dependency->new(
                         service_path => '/logging/logger'
                     ),
                 }
             )
         );

       So, as an example that can be seen above, hierarchal containers can be used as a form of
       namespacing to organize your Bread::Board configuration better. As it is shown with the
       'authenticator' service, it is possible to address services outside of your container
       using path notation. In this case the 'authenticator' service makes the assumption that
       its parent container has both a 'database' and a 'logging' sub-container and they contain
       a 'db_conn' and 'logger' service respectively. And as is shown in the 'app' service, it is
       also possible to address services using an absolute path notation.

   Sugar Layer
       So, up until now we have been creating all our Bread::Board objects by hand. As you can
       tell, this is both verbose and tedious. To make your life easier, Bread::Board provides a
       simple sugar layer over these objects. Here is the equivalent of the above Bread::Board
       configuration using the sugar layer.

         my $c = container 'app' => as {

             container 'database' => as {
                 service 'db_conn' => (
                     block => sub {
                         my $s = shift;
                         return DBI->connect(
                             $s->param('dsn'),
                             $s->param('username'),
                             $s->param('password')
                         );
                     },
                     dependencies => {
                         dsn      => ( service 'dsn'      => 'dbi:mysql:test' ),
                         username => ( service 'username' => 'user' ),
                         password => ( service 'password' => '****' ),
                     }
                 )
             };

             container 'logging' => as {
                 service 'log_file' => '/var/log/app.log';
                 service 'logger' => (
                     class        => 'Logger',
                     dependencies => {
                         log_file => depends_on('log_file'),
                     }
                 )
              };

             container 'security' => as {
                 service 'authenticator' => (
                     class => 'Authenticator',
                     dependencies => {
                         db_conn => depends_on('../database/db_conn'),
                         logger  => depends_on('../logging/logger'),
                     }
                 )
             };

             service 'app' => (
                 class => 'Application',
                 dependencies => {
                     auth    => depends_on('/security/authenticator'),
                     db_conn => depends_on('/database/db_conn'),
                     logger  => depends_on('/logging/logger'),
                 }
             )
         };

       As you can see this not only makes the code shorter, but more declarative and easier to
       read.

SEE ALSO

       This article is based on an article I wrote for The Perl Journal about my earlier IOC
       module. That article can be found online at <http://www.drdobbs.com/windows/184416179>.

AUTHOR

       Stevan Little <stevan@iinteractive.com>

COPYRIGHT AND LICENSE

       This software is copyright (c) 2014 by Infinity Interactive.

       This is free software; you can redistribute it and/or modify it under the same terms as
       the Perl 5 programming language system itself.