polygene-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Niclas Hedhman <nic...@hedhman.org>
Subject Assembly proposal
Date Wed, 13 May 2015 07:45:00 GMT
Gang, (warning; long)

Long tima ago, when the Assembly system was first created, we had very
little experience of what would be needed, and kept the API very
minimalistic. One area that was left out completely (on purpose) was to
provide a more declarative way author the application structure.

A few applications later, I keep re-implementing the same thing over and
over. It looks like this;


    public interface LayerAssembler
    {
        LayerAssembly assemble() throws AssemblyException;
    }

    public interface ModuleAssembler
    {
        ModuleAssembly assemble()
            throws AssemblyException;
    }

And then implementations of those becomes like this;

    public class DomainLayer
        implements LayerAssembler
    {
        public static final String NAME = "domain";
        private final LayerAssembly layer;

        public DomainLayer( LayerAssembly layer )
        {
            this.layer = layer;
        }

        @Override
        public LayerAssembly assemble()
            throws AssemblyException
        {
            new CommandLineModule( layer.module( CommandLineModule.NAME )
).assemble();
            new HostModule( layer.module( HostModule.NAME ) ).assemble();
            new ContainersModule( layer.module( ContainersModule.NAME )
).assemble();
            return layer;
        }
    }


    public class CommandLineModule
        implements ModuleAssembler
    {
        public static final String NAME = "domain";
        private final ModuleAssembly module;

        public CommandLineModule( ModuleAssembly module )
        {
            this.module = module;
        }

        @Override
        public ModuleAssembly assemble()
            throws AssemblyException
        {
            module.services( CommandLineExecutor.class ).visibleIn(
Visibility.layer );
            module.values( CommandLine.class );
            return module;
        }
    }

The reason for injecting the layer and module into the constructor instead
of assemble() method, is because every now and then additional arguments
might be needed.

So it is all put together as

    public class MyAppAssembler
        implements ApplicationAssembler
    {
        private static final String NAME = "My Application Name";
        private static final String VERSION = "1.0";
        private final Application application;

        public MyAppAssembler()
            throws AssemblyException
        {
            Energy4Java qi4j = new Energy4Java();
            ApplicationDescriptor model = qi4j.newApplicationModel( this );
            application = model.newInstance( qi4j.spi() );
        }

        public Application application()
        {
            return application;
        }

        public void start()
            throws ActivationException
        {
            application.activate();
        }

        public void stop()
            throws PassivationException
        {
            application.passivate();
        }

        @Override
        public ApplicationAssembly assemble( ApplicationAssemblyFactory
applicationFactory )
            throws AssemblyException
        {
            ApplicationAssembly assembly =
applicationFactory.newApplicationAssembly();
            assembly.setName( NAME );
            assembly.setName( VERSION );
            LayerAssembly infraLayer = new InfrastructureLayer(
assembly.layer( InfrastructureLayer.NAME ) ).assemble();
            LayerAssembly domainLayer = new DomainLayer( assembly.layer(
DomainLayer.NAME ) ).assemble();
            LayerAssembly serviceLayer = new ServiceLayer( assembly.layer(
ServiceLayer.NAME ) ).assemble();
            LayerAssembly connectivityLayer = new ConnectivityLayer(
assembly.layer( ConnectivityLayer.NAME ) ).assemble();

            connectivityLayer.uses( serviceLayer );
            serviceLayer.uses( domainLayer );
            serviceLayer.uses( infraLayer );
            domainLayer.uses( infraLayer );
            return assembly;
        }
    }


So, first I would like to add the ModuleAssembler and LayerAssembler as
Core Bootstrap classes. Secondly, I would to create a abstract class,
perhaps named LayeredApplicationAssembler, which encapsulates the
boilerplate code, and defines a "Layer DSL", maybe something like this;

    public class MyAppAssembler extends LayeredApplicationAssembler
    {
        public MyAppAssembler( String name, String version, Mode mode )
        {
            super( name, version, mode );
        }

        protected void onAssembly()
        {
            LayerAssembly infraLayer = layer().definedBy( new
InfrastructureLayer() );
            LayerAssembly domainLayer = layer().definedBy( new
DomainLayer() ).uses( infraLayer );
            LayerAssembly serviceLayer = layer().definedBy( new
ServiceLayer() ).uses( domainLayer, infraLayer );
            layer().definedBy( new ConnectivityLayer() ).uses( serviceLayer
);
        }
    }

IMHO, this is quite a nice improvement over the current flexibility that
exists.

But, I have also previously mentioned that an annotated solution might be
even cooler.

public class MyAppAssembler extends AnnotatedApplicationAssembler
{
    @Uses( ServiceLayer.class )
    interface ConnectivityLayer
    {
        ModuleAssembler MODULES = {
            RestModule.class,
            WebAppModule.class
        };
    }

    @Uses( { DomainLayer.class, InfrastructureLayer.class } )
    interface ServiceLayer
    {
        ModuleAssembler MODULES = {
            DeploymentModule.class,
            ManagementModule.class,
            MonitoringModule.class
        };
    }

    @Uses( InfrastructureLayer.class )
    interface DomainLayer
    {
        ModuleAssembler MODULES = {
            CommandLineModule.class,
            HostsModule.class,
            ContainersModule.class
        };
    }

    interface InfrastructureLayer
    {
        ModuleAssembler MODULES = {
            StorageModule.class,
            ConfigModule.class,
            IndexingModule.class
        };
    }
}

The entire application structure overview captured in a single document,
with all the implementation detail noise removed.
One feature that is not present without anything additional is that via the
LayerAssembly interface, it is possible to apply cross-cutting concerns on
Composites across an entire layer. I suggest that this is provided via
method call in the AnnotatedApplicationAssembler super type. For instance,
let's say that we want to apply the UnitOfWorkPropagationConcern to all
Services in the service layer.

public class MyAppAssembler extends AnnotatedApplicationAssembler
{
    :  // all the above at the top...

    public MyAppAssembler( String name, String version, Mode mode )
    {
        super( name, version, mode );
        withConcerns( UnitOfWorkPropagationConcern.class ).inLayer(
ServiceLayer.class );
    }
}

Looks neat enough to me, and should be able to be expanded for all the
features that are currently possible.

FINALLY,
IF the AnnotatedApplicationAssembler also provides a complete bootstrapper,
and we would only add

    public static void main( String[] args )
    {
        new MyAppAssembler().run(args);
    }

then we could start things from command line, provide an abstraction for
Application Arguments, which would also be available from a
WebApplicationAssembler (probably a subclass of the above).
And I am also toying with the idea that optional modules could be provided
from the commandline, such as which entity store to be used, enabling JMX
or Tracing, and other modules that can be made completely optional.


WDYAT?


Cheers
--
Niclas Hedhman, Software Developer
http://zest.apache.org - New Energy for Java

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message