<br><br><div class="gmail_quote">On Tue, Jan 10, 2012 at 1:18 PM, Jason Galea <span dir="ltr">&lt;<a href="mailto:lists@eightdegrees.com.au">lists@eightdegrees.com.au</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

hehe.. you want layers, I got layers..</blockquote><div><br></div><div>I just got out of yet another meeting about this architecture redesign.   (I&#39;d like to see the graph that relates productivity to the number of people involved some day...)</div>

<div><br></div><div>Jason, this is probably a question best for you and your experience, but I (ignoring that graph above) would like to hear other&#39;s opinions and reasoning.</div><div><br></div><div><br></div><div>My goal was to put a layer between Catalyst and DBIC for a few reasons, including:</div>

<div><ol><li>To have a place to put common model code that cannot be represented in DBIC (e.g. data from other sources)</li><li>To be able to split up the model into logical units that can be tested and used independently.</li>

<li>To abstract out the physical layout of the database -- be able to change data layer w/o changing the API.</li></ol></div><div><br></div><div>My idea was that Catalyst would call a method in the new model layer and possibly get a DBIC object back.  There is concern from some at my meeting that we don&#39;t want to give the Catalyst app developer a &quot;raw&quot; DBIC object and that we should wrap it (as it appears you are doing, Jason) in yet another object.   That is, we want to allow $user-&gt;first_name, but not $user-&gt;search_related or $user-&gt;delete.</div>

<div><br></div><div>That requires writing new wrapper classes for every possible result -- not just mirroring DBIC&#39;s result classes but possibly many more because the new model might have multiple calls (with different access levels) for fetching user data.  That is, $user-&gt;email might work for some model methods that return a user but not methods called on the model.</div>

<div><br></div><div>Frankly, to me this seems like a lot of code and work and complexity just to prevent another developer from doing something stupid -- which we cannot prevent anyway.  And smart programmers can get at whatever they want, regardless.  Seems more risky to make the code more complex and thus harder to understand.  The cost/benefit ratio just doesn&#39;t seem that great.</div>

<div><br></div><div>Am I missing something?</div><div><br></div><div><br></div><div>I suppose this is not unlike the many discussions about what to pass to the view.  Does the controller, for example, fetch a user object and pull the data required for the view into a hash and then pass that to the view?  Or does the controller just fetch a user object and pass that directly to the view to decide what needs to display?</div>

<div><br></div><div>I prefer just passing the object to the view.  The controller code is much cleaner and then when the view needs to change don&#39;t need to also change the controller.  And when there&#39;s a different view (like an API or moble ) the same controller action can be used.</div>

<div><br></div><div>Thanks,</div><div><br></div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><br></div><div>In addition to everything already mentioned I wanted to get Bread::Board in on the act..</div>

<div><br></div><div>I&#39;ve put Lecstor up on GitHub if you&#39;re interested along with a Catalyst app that uses it. Neither really do much but boy is there a lot of scaffolding! ..and all the tests pass! 8)</div>
<div><br></div><div>The basic hook-up is Catalyst -&gt; Catalyst Models -&gt; Bread::Board Containers -&gt; Lecstor App/Models -&gt; DBIC and others.. decoupled like a broken bag o marbles..</div><div><br></div><div>the Catalyst Models:</div>


<div> - LecstorApp - application lifetime</div><div> - LecstorModel - application lifetime</div><div> - LecstorRequest - request lifetime</div><div> - Lecstor - request lifetime</div><div><br></div><div>LecstorModel &amp; LecstorRequest return Bread::Board containers.</div>


<div>LecstorApp returns a parameterized Bread::Board container</div><div>Lecstor grabs the first two and shoves them into the third to make another Bread::Board container from which I get my app..</div><div><br></div><div>


have I gone walkabout!?</div><div><br></div><div><a href="https://github.com/lecstor/Lecstor" target="_blank">https://github.com/lecstor/Lecstor</a></div><div><br><a href="https://github.com/lecstor/Lecstor-Shop-Catalyst" target="_blank">https://github.com/lecstor/Lecstor-Shop-Catalyst</a></div>


<div><br></div><div>comments welcome.</div><div><br></div><div>cheers,</div><div><br></div><div>J<div><div class="h5"><br><br><div class="gmail_quote">On Tue, Jan 10, 2012 at 12:16 AM, Jason Galea <span dir="ltr">&lt;<a href="mailto:lists@eightdegrees.com.au" target="_blank">lists@eightdegrees.com.au</a>&gt;</span> wrote:<br>


<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br><br><div class="gmail_quote"><div>On Mon, Jan 9, 2012 at 3:14 PM, Bill Moseley <span dir="ltr">&lt;<a href="mailto:moseley@hank.org" target="_blank">moseley@hank.org</a>&gt;</span> wrote:<br>


<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

<div><br><br>On Monday, January 2, 2012, Jason Galea  wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="gmail_quote"><div><br></div>
<div>I think I&#39;ve added another layer but I&#39;m not sure where you draw the line.. I have a model layer over DBIC pulling together related result classes under a single model class. Then the app? layer uses the model layer to get things done. So I&#39;d probably have one &quot;distribution&quot; that is our DBIC wrapped in a model layer layer and use that in a number of apps.. 8) Each app can then be used as the single model in a Catalyst app or script or whatever.. (I think I need more names for the parts..)</div>





</div></div></blockquote><div><br></div></div><div>Yes, where to draw the line is difficult to know.   I&#39;ve only had a few hours to work on this but already I feel like I&#39;m reinventing Catalyst -- mostly because my model layer is pulling in much of the components that my Catalyst app would normally do -- DBIC, caching, even some concept of the &quot;current user&quot;.   Access control is another topic.</div>




</blockquote><div><br></div></div><div>The problem parts for me are DBIC and TT. I thought I could just set up the components as usual, then load my app with them but it get&#39;s tricky calling one component from another at setup time, although it all works fine if you instantiate the app per request. So now I&#39;m connecting/creating those myself.</div>




<div><br></div><div>For other things provided by plugins I&#39;m working more with Catalyst so for caching I will probably have my app accept a cache object at construction and pass in the Catalyst cache. For Authentication I&#39;ve created my own store and user for the Catalyst Authentication plugin and they use my app to do what they have to. I&#39;ve also created a store for the session plugin which uses my app, so all-in-all my app can see/touch everything that Catalyst is doing, and I can still make use of all the Catalyst stuff available (hopefully).</div>


<div>

<div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">




<div><div class="gmail_quote"><div><div>I have &quot;Sets&quot; in lu of ResultSets and &quot;Models&quot; for Results. Although in most instances a Model will actually cover the usage of multiple Results. Each Set gets the dbic schema object and knows it&#39;s resultset name. Each model has a data attribute which contains a dbic row object and &quot;handles&quot; any methods I don&#39;t need to override via the Moose &quot;handles&quot; attribute attribute!?</div>






<div><br></div><div>Set-&gt;create($hash) creates the dbic object and stuffs it into a model class and returns that.</div></div></div></div></blockquote><div><br></div></div><div>So you are mirroring DBICs class structure a bit.  I need to consider that approach more as currently my model layer returns the DBIC row object directly.  So, I have something like this:<br>





<br></div><div>my $user_model = Model::User-&gt;new;</div><div>my $new_user = $user-&gt;new_user( $user_data );</div></blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">




<div><br></div><div>Not as flexible as your approach but my goal currently is to just abstract out the ORM so that Model::User can hide the specifics of the database.   Actually, it&#39;s not that hard to do directly with DBIC, either.</div>




</blockquote><div><br></div></div><div>yeh, I decided a while back that DBIx::Class is complicated enough and I&#39;m too lazy to keep trying to work out complicated solutions in the DBIC classes to do things I know I can do quickly and easily with regular Moose classes.. and I like having nice clean DBIC classes..</div>


<div>

<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">




<div><div class="gmail_quote"><div><div>Each result class that has a model class overrides it&#39;s inflate_result method which again stuffs the dbic row object into the model object so searches on the related dbic resultsets return my model objects.</div>





</div></div></div></blockquote><div><br></div></div><div>Can you show a code example of that?  I&#39;m not sure I&#39;m following why you use that approach instead of having your layer on top of DBIC do that.</div></blockquote>




<div><br></div></div><div> and the exception to the rule.. I did have my Set classes (which I now refer to as Model Controllers) grabbing search results and looping through, inflating them all into my Model Instances but then I couldn&#39;t just grab a resultset if I needed to limit/restrict/whatever, and any search or find had to be put through that ringer. With inflate_result I know that no matter how I get the results they&#39;ll be instances of my model. create is the only thing that doesn&#39;t work for so my controller does the wrapping there.</div>




<div><br></div><div><div>package Lecstor::Schema::Result::Person;</div><div>use base qw/DBIx::Class/;</div><div>__PACKAGE__-&gt;load_components(qw/ Core /);</div><div>__PACKAGE__-&gt;table(&#39;person&#39;);</div><div>__PACKAGE__-&gt;add_columns(&#39;id&#39; ,&#39;firstname&#39;,&#39;surname&#39;);</div>




<div>__PACKAGE__-&gt;set_primary_key(&#39;id&#39;);</div><div><br></div><div>sub inflate_result {</div><div>    my $self = shift;</div><div>    my $ret = $self-&gt;next::method(@_);</div><div>    return unless $ret;</div>




<div>    return Lecstor::Model::Instance::Person-&gt;new( _record =&gt; $ret );</div><div>}</div><div><br></div><div>1;</div></div><div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">




<div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="gmail_quote"><div>
<div><br></div><div>Each model class has a validation class based on.. Validation::Class and create &amp; update run their input through that. If there are errors I stuff the errors into a very basic exception object and return that. This way I can return the same exception object no matter where the error comes from, eg a dbic exception..</div>





</div></div></div></blockquote><div><br></div></div><div>Yes, I&#39;m doing something very similar where validation happens before the method in the model and on validation errors and exception is thrown (if you are on the Moose list you may have seen my example).</div>





<div><br></div><div>Thanks for the feedback and the ideas,</div></blockquote><div><br></div></div><div>no worries at all, happy to be able to provide it.</div><div><br></div><div>cheers,</div><div><div><br></div>
<div>J</div><div> </div>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<span><font color="#888888"><div> </div><br><br>-- <br>Bill Moseley<br><a href="mailto:moseley@hank.org" target="_blank">moseley@hank.org</a><br>
</font></span><br>_______________________________________________<br>
List: <a href="mailto:Catalyst@lists.scsys.co.uk" target="_blank">Catalyst@lists.scsys.co.uk</a><br>
Listinfo: <a href="http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst" target="_blank">http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst</a><br>
Searchable archive: <a href="http://www.mail-archive.com/catalyst@lists.scsys.co.uk/" target="_blank">http://www.mail-archive.com/catalyst@lists.scsys.co.uk/</a><br>
Dev site: <a href="http://dev.catalyst.perl.org/" target="_blank">http://dev.catalyst.perl.org/</a><br>
<br></blockquote></div></div><br>
</blockquote></div><br></div></div></div>
<br>_______________________________________________<br>
List: <a href="mailto:Catalyst@lists.scsys.co.uk">Catalyst@lists.scsys.co.uk</a><br>
Listinfo: <a href="http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst" target="_blank">http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst</a><br>
Searchable archive: <a href="http://www.mail-archive.com/catalyst@lists.scsys.co.uk/" target="_blank">http://www.mail-archive.com/catalyst@lists.scsys.co.uk/</a><br>
Dev site: <a href="http://dev.catalyst.perl.org/" target="_blank">http://dev.catalyst.perl.org/</a><br>
<br></blockquote></div><br><br clear="all"><div><br></div>-- <br>Bill Moseley<br><a href="mailto:moseley@hank.org" target="_blank">moseley@hank.org</a><br>