Niclas Hedhman a écrit :
> Gang, (warning; long)
Took me a while to ingest. Some comments inline.
> 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.
This LGTM and should work well with existing helpers in Assemblers
(config/identity/visibility).
> 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.
Looks neat. Kind of magic. Shouldn't we wait to master assembly using
Java 8 constructs before adding something like that?
That's it for now.
Cheers
/Paul