tag:robotlegs.tenderapp.com,2009-10-18:/discussions/questions/102-should-a-service-be-a-bubble-or-individual-piecesRobotlegs: Discussion 2018-10-18T16:35:10Ztag:robotlegs.tenderapp.com,2009-10-18:Comment/12566062010-03-19T04:18:31Z2010-03-19T04:18:31ZShould a service be a 'bubble' or individual pieces?<div><p>Inject ILessonDataProcessor concrete LessonXMLProcessor into the
service. Service will always need to process data. I think that
these classes are intimate and that convolution of the
<em>necessary</em> coupling is bad. Loose coupling is great, but I
think that you can overdo it. The drawback to loose coupling is
loss of clarity and intent, making it harder for others to
understand the code. I think it is admirable across tiers, for the
most part, but a processor that is only going to be used by the
service doesn't need to be on an island. :></p></div>Joel Hookstag:robotlegs.tenderapp.com,2009-10-18:Comment/12566062010-03-19T10:25:07Z2010-03-19T10:25:07ZShould a service be a 'bubble' or individual pieces?<div><p>Ah ... clarity.</p>
<p>Thanks Joel - I agree that you can overdo the loose
coupling.</p>
<p>I woke in the night with a suddenly recognition that if no other
classes in the whole app give a shit that an event is being fired,
the LessonXMLLoader has got no business firing it on the shared
event bus when it could just handle it itself.</p>
<p>I can feel a coupling decision flowchart brewing...</p></div>Straytag:robotlegs.tenderapp.com,2009-10-18:Comment/12566062010-03-19T11:37:27Z2010-03-19T11:58:24ZShould a service be a 'bubble' or individual pieces?<div><p>Just to chime in,</p>
<p>My approach is essentially the same as Joel's. There is an
endpoint (Service URL) which contains a serialized representation
of a FooValueObject (ie: JSON, XML, AMF, etc).</p>
<p>The simplest member of the process is the FooParser. The Parsers
job is to simply convert the serialised data into a FooValueObject,
the interface looks like this:</p>
<pre>
<code>public interface FooParser {
function parse(data : *) : FooValueObject;
}</code>
</pre>
<p>And a typical implementation looks like this:</p>
<pre>
<code>public class FooJSONParser implements FooParser {
public function parse(data : *) : FooValueObject
{
const values : Object = new JSONDecoder(data).getValue();
const foo : FooValueObject = new FooValueObject();
// The parser can now copy the data from the Dynamic object to the strongly typed ValueObject.
foo.property = values.property;
return foo;
}
}</code>
</pre>
<p>The Service class will have a FooParser injected into it via the
FooParser interface, for example:</p>
<pre>
<code>[Inject]
public var parser : FooParser;</code>
</pre>
<p>The Service makes use of a URLLoader and, in the onCompleteEvent
block (ie: when the remote data has finished loading), it will call
the parse() method of the FooParser. If the parsing completes
without an exception, we can dispatch a FooServiceEvent back to the
framework (and the value object can be set on a Model). However,
should an error be thrown, a FooServiceErrorEvent will notify the
system that we have a problem!</p>
<pre>
<code>private function onCompleteEvent(event : Event) : void {
removeLoaderEventListeners();
try {
const fooValueObject : FooValueObject = parser.parse(_loader.data);
dispatch(new FooServiceEvent(FooServiceEvent.COMPLETE, fooValueObject));
}
catch (e : Error) {
dispatch(new FooServiceErrorEvent(FooServiceErrorEvent.ERROR, e.message));
}
}</code>
</pre>
<p>This approach proves very flexible as it allows you to swap the
FooParser implementation both at compile time and at run time. When
you first build your application, the Back End team may decide to
make the endpoint spit out XML data, so your injector would be
setup like this:</p>
<pre>
<code>injector.mapClass(FooParser, FooXMLParser);</code>
</pre>
<p>However, just before release they decide that XML is old hat and
that JSON is the new cool - no problem, just write a JSON Parser
and update your injection mapping:</p>
<pre>
<code>injector.mapClass(FooParser, FooJSONParser);</code>
</pre>
<p>This can even be taken one step further to allow you to pick
which Parser gets used a run-time with the use of a Factory Method.
Say that you Back End team are very indecisive and can't make up
their mind if they are going to use XML or JSON; fine, we can
create a new implementation of the FooService interface which
automatically decides which parser to use based on the incoming
data, for example:</p>
<pre>
<code>public class FooParserFactory implements FooParser {
public function parse(data : *) : FooValueObject {
if (data is XML) {
return new FooXMLParser().parse(data);
}
else {
return new FooJSONParser().parse(data);
}
}
}</code>
</pre>
<p>Jonny.</p></div>Jonny Reevestag:robotlegs.tenderapp.com,2009-10-18:Comment/12566062010-03-19T11:43:55Z2010-03-19T11:43:55ZShould a service be a 'bubble' or individual pieces?<div><p>Nice one - thanks for sharing so much detail, very much
appreciated.</p>
<p>Also made me realise that I should use a separate Error event
and not just rely on a different event type for success /
failure.</p>
<p>This stuff is where RL really comes into its own I think.</p></div>Straytag:robotlegs.tenderapp.com,2009-10-18:Comment/12566062010-03-19T14:49:38Z2010-03-19T14:49:38ZShould a service be a 'bubble' or individual pieces?<div><p>"you should not target low coupling, instead target appropriate
coupling" - Mike Labriola (@mlabriola)</p>
<p>That was the quote I was searching for :></p></div>Joel Hookstag:robotlegs.tenderapp.com,2009-10-18:Comment/12566062010-03-19T22:14:25Z2010-03-19T22:14:25ZShould a service be a 'bubble' or individual pieces?<div><p>That is a very, very good quote.</p></div>Stray