Assembly

Assembly is the name of the boot-strap phase, where the application Structure is declared (programmatically). The Assembly will be consumed by the ApplicationBuilder, which produces an Application instance. This instance does not contain any custom objects other than metaInfo instances, and is fully serializable if the metaInfos are. All the application structure has been built, all the layers and modules been wired up, and all the sub-composite structures are in place to quickly instantiate the various parts of the application.

Once the initialization phase is complete, the bootstrap controller will call the Application.activate() method to start things up.

Recap of sequence;

  • Create, obtain or lookup Assemblers.
  • Establish the application structures by creating an ApplicationAssembly.
  • Create a Qi4j Model instance.
  • Initialize the Model (if any).
  • Create a Qi4j application instance.
  • Do the initialization of the application.
  • Call activate() on the application instance.

Luckily these steps have been simplified with convenience decorators, so that your code becomes simpler.

There are 3 levels of convenience provided, depending on your needs. Singelton, "Pancake"-layering and Full assembly.

Singleton Assembly


Every Qi4j runtime instance consist of One Application, with one or more Layers and one or more Modules in each Layer. So the minimal application is still one layer with one module. This is not recommended other than for testing purposes and really trivial applications.

Let's take a closer look at how it is put together.

SingletonAssembler assembler = new SingletonAssembler()
{
    public void configure( ModuleAssembly module )
        throws AssemblyException
    {
        module.addServices( MyService.class ).identifiedBy( "Foo" ) );
        module.addServices( MyService.class ).identifiedBy( "Bar" ) );
        module.addObjects( Stuff.class );
    }
};

ObjectBuilderFactory factory = assembler.objectBuilderFactory();
Stuff stuff = factory.newObjectBuilder( Stuff.class ).newInstance();

Once the SingletonAssembler constructor returns, the Qi4j application is up and running.

The SingletonAssembler also makes common system resources available from the bootstrap code, such as ObjectBuilderFactory, UnitOfWorkFactory and others. This is possible since there is only one Module.

"Pancake" Layer Assembly


There is one case that stands out as a common case, and forms a reasonable middle-ground. It is where each layer sits exactly on top of each other layer, like pancakes. Each layer will only use the layer directly below and only that layer. For this case we have a convenience setup. You create an Assembler[][][], where the outer-most array is each layer, the middle array is the modules in each layer, and the last array is a set of assemblers needed to put the things togather. Let's look at an example;
public static void main( String[] args )
    throws Exception
{
    qi4j = new Energy4Java();
    Assembler[][][] assemblers = new Assembler[][][]
        {
            { // View Layer
              { // Login Module
                new LoginAssembler(),
                 :
              },
              { // Main Workbench Module
                new MenuAssembler(),
                new PerspectivesAssembler(),
                new ViewsAssembler(),
                  :
              },
              { // Printing Module
                new ReportingAssembler(),
                new PdfAssembler(),
                  :
              }
            },
            { // Application Layer
              { // Accounting Module
                new BookkeepingAssembler(),
                new CashFlowAssembler(),
                new BalanceSheetAssembler( ),
                :
              },
              { // Inventory Module
                new PricingAssembler(),
                new ProductAssembler(),
                  :
              }
            },
            { // Domain Layer
              // :  
            },
            { // Infrastructure Layer
              // :
            }
        };

    ApplicationModelSPI model = newApplication( assemblers );
    ApplicationSPI runtime = model.newInstance( qi4j.spi() );
    runtime.activate();
}

private static ApplicationModelSPI newApplication(
    final Assembler[][][] assemblers )
    throws AssemblyException
{
    return qi4j.newApplicationModel( new ApplicationAssembler()
    {
        public ApplicationAssembly assemble(
            ApplicationAssemblyFactory factory )
            throws AssemblyException
        {
            return factory.newApplicationAssembly( assemblers );
        }
    } );
}

Full Assembly


Full Assembly means that you have the opportunity to create any layer/module hierarchy that are within the rules of the Qi4j runtime. It requires more support in your code to be useful, and the example below is by no means a recommended way to organize large application assemblies.

In principle, you first start the Qi4j runtime, call newApplication with a ApplicationAssembler instance and call activate() on the returned application. The ApplicationAssembler instance will be called with a ApplicationFactory, which is used to create an ApplicationAssembly describing the application structure.

private static Energy4Java qi4j;
private static ApplicationSPI application;

public static void main( String[] args )
    throws Exception
{
    // Create a Qi4j Runtime
    qi4j = new Energy4Java();
    application = qi4j.newApplication( new ApplicationAssembler()
    {
        public ApplicationAssembly assemble(
            ApplicationAssemblyFactory applicationFactory )
            throws AssemblyException
        {
            ApplicationAssembly assembly =
                applicationFactory.newApplicationAssembly();
            buildAssembly( assembly );
            return assembly;
        }
    } );

    // activate the application
    application.activate();
}

static void buildAssembly( ApplicationAssembly app )
{
    LayerAssembly webLayer = createWebLayer( app );
    LayerAssembly domainLayer = createDomainLayer( app );
    LayerAssembly persistenceLayer = createInfrastructureLayer( app );
    LayerAssembly authLayer = createAuth2Layer( app );
    LayerAssembly messagingLayer = createMessagingLayer( app );
    webLayer.uses( domainLayer );
    domainLayer.uses( authLayer );
    domainLayer.uses( persistenceLayer );
    domainLayer.uses( messagingLayer );
}

static LayerAssembly createWebLayer( ApplicationAssembly app )
{
    LayerAssembly layer = app.newLayerAssembly( "web-layer" );
    createCustomerWebModule( layer );
    return layer;
}

static LayerAssembly createDomainLayer( ApplicationAssembly app )
{
    LayerAssembly layer = app.newLayerAssembly( "domain-layer" );
    createCustomerDomainModule( layer );
    // :
    // :
    return layer;
}

static LayerAssembly createInfrastructureLayer( ApplicationAssembly app )
{
    LayerAssembly layer = app.newLayerAssembly( "infrastructure-layer" );
    createPersistenceModule( layer );
    return layer;
}

static LayerAssembly createMessagingLayer( ApplicationAssembly app )
{
    LayerAssembly layer = app.newLayerAssembly( "messaging-layer" );
    createWebServiceModule( layer );
    createPersistenceModule( layer );
    return layer;
}

static LayerAssembly createAuth2Layer( ApplicationAssembly application )
{
    LayerAssembly layer = application.newLayerAssembly( "auth2-layer" );
    createAuthModule( layer );
    return layer;
}

static void createCustomerWebModule( LayerAssembly layer )
{
    ModuleAssembly assembly =
        layer.newModuleAssembly( "customer-web-module" );
    assembly.addTransients( CustomerViewComposite.class );
    assembly.addTransients( CustomerEditComposite.class );
    assembly.addTransients( CustomerListViewComposite.class );
    assembly.addTransients( CustomerSearchComposite.class );
}

static void createCustomerDomainModule( LayerAssembly layer )
{
    ModuleAssembly assembly =
        layer.newModuleAssembly( "customer-domain-module" );
    assembly.addEntities( CustomerEntity.class );
    assembly.addEntities( CountryEntity.class );
    assembly.addTransients( AddressComposite.class );
}

static void createAuthModule( LayerAssembly layer )
{
    ModuleAssembly assembly = layer.newModuleAssembly( "auth-module" );
    assembly.addAssembler( new LdapAuthenticationAssembler() );
    assembly.addAssembler( new ThrinkAuthorizationAssembler() );
    assembly.addAssembler( new UserTrackingAuditAssembler() );
}

static void createPersistenceModule( LayerAssembly layer )
{
    ModuleAssembly assembly =
        layer.newModuleAssembly( "persistence-module" );
    // Someone has created an assembler for the Neo EntityStore
    NeoAssembler neo = new NeoAssembler( "./neostore" );
    assembly.addAssembler( neo );
}


Qi4j and the Qi4j logo are trademarks of Richard Öberg, Niclas Hedhman and the members of the Qi4j Core Team. See Qi4j licensing for more information.
Powered by SiteVisionexternal link.