tag:robotlegs.tenderapp.com,2009-10-18:/discussions/solutions/10-feature-composition-by-bootstrap-commandRobotlegs: Discussion 2018-10-18T16:35:21Ztag:robotlegs.tenderapp.com,2009-10-18:Comment/47740082011-01-17T16:21:28Z2011-01-17T16:21:28ZFeature composition by bootstrap Command<div><p>My project is modular. There's a main application - an
e-learning environment - which varies according to which mix of
modules each user has.</p>
<p>There are also a couple of toolsets - again modular - which the
managers and administrators use to change stuff and view reports
etc.</p>
<p>In the admin tool we added a feature which lets the
administrator launch the personal-learning report for any user.</p>
<p>The feature has been well received and they've decided that they
want any user who 'manages' other users to be able to use this
within the main application - to launch reports for the people they
manage as well as themselves. These user/manager relationships are
already part of the system, so bringing in a list of managed users
to populate this stuff isn't a problem.</p>
<p>In fishing through the admin module to work out what I need to
bring over into the main application menu module (where this will
live) I've realised that I can extract the required robotlegs
wirings into a single bootstrap Command, and then just include that
in the main menu module.</p>
<p>Normally I bootstrap by 'type' - I'll have bootstraps for
Commands, Services, Models and so on. But I realised that it must
be possible to bootstrap by 'feature'.</p>
<p>This 'managed users reports' bootstrap now contains:</p>
<ul>
<li>A mediator mapping for the view/mediator that lets them select
a user and launch the report</li>
<li>A command mapping for the command that picks up the event
dispatched by this mediator and makes the url request</li>
<li>A singleton service mapping for the factory that creates the
list of user VOs from the xml</li>
</ul>
<p>To include the feature in the main menu module I now just have
to include this bootstrap command and hook up to use the factory
for this data and add the correct view.</p>
<p>It really brings home the value of coding to interface contracts
as well - if I want to vary the factory then I just switch in a
different concrete implementation.</p>
<p>Is anyone else using bootstrap-by-feature? I'm thinking that I
might play with this being my default approach to bootstrapping.
Are there any gotchas?</p></div>Straytag:robotlegs.tenderapp.com,2009-10-18:Comment/47740082011-01-17T16:40:14Z2011-01-17T16:40:14ZFeature composition by bootstrap Command<div><p>Thanks for the interesting discussion of bootstrapping,
Stray!</p>
<p>This meshes nicely with the ideas that were brought up in<br>
<a href=
"http://knowledge.robotlegs.org/discussions/suggestions/36-postmortem-mvcs-folder-structure-sucks-suggestion-use-a-modular-folder-structure-tied-to-your-project">
http://knowledge.robotlegs.org/discussions/suggestions/36-postmorte...</a></p>
<p>The more I ponder these structuring-issues, the more structuring
by<br>
type instead of feature reminds me of using Hungarian Notation as
a<br>
variable naming convention. Including the bad taste that leaves in
my<br>
mouth ...</p></div>Till Schneidereittag:robotlegs.tenderapp.com,2009-10-18:Comment/47740082011-01-17T17:45:28Z2011-01-17T17:45:28ZFeature composition by bootstrap Command<div><p>I love by-module-by-type-by-feature! So Nyer... Or to put it
another way:</p>
<ol>
<li>My modules are my major features, as fine grained as they need
to be architecturally. I structure folders by module, mostly flat
in the modules dir. Components are also treated similarily (they
only differ by not using RL child contexts)<br></li>
<li>My types (Views/Services/Controller/Model) are best layed out
by type, not feature. Within each type we'll then 'heuristically'
decide to namespace based on feature. These 'features' can be
grouped differently for each type. eg module.view.userForm. <em>and
module.view.userList.</em> will both consume
module.model.user.*</li>
</ol>
<p>Meanwhile, as for bootstrapping, for the same reason as my
preference for module/type/feature (in that order), I bootstrap
much the same way. Here comes the freakshow, I mean code:</p>
<pre>
<code>public class ConfigurationSequence extends AbstractSequencer {
// One of the few places I use //'s over SRP.
// Perhaps ISequencer should have a placeholder addGroup?
override protected function configure():void {
// Modelling
addStep(ConfigureWorkersModelCommand);
addStep(ConfigureScalesModelCommand);
addStep(ConfigureTimelineDivisionModelCommand);
addStep(ConfigureModuleStateCommand);
addStep(ConfigureJobVOModelCommand);
addStep(ConfigureAssignmentVOCommand);
addStep(ConfigureWorkerJobModelCommand);
addStep(ConfigureJobsModelCommand);
addStep(ConfigureJobSelectionModelCommand);
addStep(ConfigureJobDraggingModel);
addStep(ConfigureWindowModelCommand);
addStep(ConfigureJobsBusyModelCommand);
addStep(ConfigureJobVOProxyCommand);
// Services
addStep(ConfigureJobServicesCommand);
// Commands
addStep(ConfigureCommands);
addStep(MapIncomingCommands);
addStep(MapOutgoingCommands);
// Views
addStep(ConfigureScrollpadsCommand);
addStep(ConfigureDatePickViewCommand);
addStep(ConfigureScheduleGridViewCommand);
addStep(ConfigureScheduleHeaderViewCommand);
addStep(ConfigureScheduleToolViewCommand);
addStep(ConfigureTimeIndicatorViewCommand);
addStep(ConfigureContextViewCommand);
}
}</code>
</pre>
<p>That is the bootstrap sequence code for a single module.</p>
<p>Each step added, bootstraps a "by-type-by-feature". Of which,
there are many :P<br>
If you wanted to 'pull out a feature' then I'd imagine it'd either
be a candidate for 'Extract Module/Component' or simpler, something
that just needs five minutes of bootstrap cherry picking.</p></div>squeedeetag:robotlegs.tenderapp.com,2009-10-18:Comment/47740082011-01-17T18:18:28Z2011-01-17T18:18:28ZFeature composition by bootstrap Command<div><p>Nice!</p>
<p>While we're sharing freakshows... I mean code... in the small
strategy game I just built the main context has:</p>
<pre>
<code>// Map some Commands to Events
commandMap.mapEvent(ContextEvent.STARTUP_COMPLETE, BootstrapGameStartup, ContextEvent, true);
commandMap.mapEvent(ContextEvent.STARTUP_COMPLETE, StartGameCommand, ContextEvent, true);</code>
</pre>
<p>And then BootstrapGameStartup does...</p>
<pre>
<code>commandMap.mapEvent(GameEvent.GAME_STARTED, BootstrapModels, GameEvent, true);
commandMap.mapEvent(GameEvent.GAME_STARTED, BootstrapViewMediators, GameEvent, true);
commandMap.mapEvent(GameEvent.GAME_STARTED, BootstrapDayCycleCommands, GameEvent, true);
commandMap.mapEvent(GameEvent.GAME_STARTED, BootstrapGameEndingsCommand, GameEvent, true);
commandMap.mapEvent(GameEvent.GAME_STARTED, StartViewCommand, GameEvent, true);
commandMap.mapEvent(GameEvent.GAME_STARTED, ConfigureModelsCommand, GameEvent, true);
commandMap.mapEvent(GameEvent.GAME_STARTED, ProcessDayStartCommand, GameEvent, true);</code>
</pre>
<p>And then the various Bootstraps in there each do their
thang.</p>
<p>But boy, I can't believe I missed the need for a sugar
method!</p>
<pre>
<code>addStep(CommandToBeBootstrapped)</code>
</pre>
<p>FTW!</p>
<p>I shall be stealing that in approximately 2 minutes.</p>
<p>Thank god this board isn't troll-tastic, or we'd be inundated
with statements about how much "more code" this kind of solution
is.</p></div>Straytag:robotlegs.tenderapp.com,2009-10-18:Comment/47740082011-01-17T18:52:17Z2011-01-17T18:52:17ZFeature composition by bootstrap Command<div><p>If you want it: <a href=
"https://gist.github.com/783251">https://gist.github.com/783251</a></p></div>squeedeetag:robotlegs.tenderapp.com,2009-10-18:Comment/47740082011-01-17T19:07:56Z2011-01-17T19:07:56ZFeature composition by bootstrap Command<div><p>Thanks - that's nice!</p>
<p>I was even thinking of just adding an addStep(... ) sugar
function to my bootstrapper to take care of mapping against the
STARTUP_COMPLETE event or whatever - but your abstract sequencer is
even nicer.</p>
<p>Cheers!</p></div>Straytag:robotlegs.tenderapp.com,2009-10-18:Comment/47740082011-01-17T23:26:51Z2011-01-17T23:41:23ZFeature composition by bootstrap Command<div><p>You could even make distinction between mandatory and optional
features where failing mandatory features will stop the application
running. While failing optional features silently get ignored by
the application. This way you can have a feature which loads
configuration files optionally and a mandatory feature which deals
with asset management.</p>
<p>Even you could then add some sugar and define at which moment
the feature needs to be prepared. For example, during the
preloading part or when during application launch time.</p>
<p>A simple feature could be:</p>
<pre>
<code>public class InternationalisationFeature extends FeatureCommand {
public function execute(): void {
injector.mapSingletonOf(II18nService, YAMLI18NService );
nextFeature();
}
}</code>
</pre>
<p>The features could then be configured in the application context
or something:</p>
<pre>
<code>public function configureFeatures(): void {
addFeature( InternationalisationFeature, Feature.PREPARE );
}</code>
</pre>
<p>Not sure yet, what the best way is to deal with services because
in this cause you would like to continue to the next step when the
service is loaded and the model is ready prepared. I guess you can
map it as a singleton after you manually instantiated it via
injector. Something like:</p>
<pre>
<code>public function execute(): void {
var service: InternationalisationFileService = new InternationalisationFileService();
eventDispatcher.addEventListener( InternationalisatonEvent.LOADED, onServiceLoaded );
service.load( 'de.yaml' );
}
private function onServiceLoaded(event: Event): void {
super.nextFeature(); // like sequencer.step();
}</code>
</pre></div>Weyert