Architectural considerations and ideas

5 views
Skip to first unread message

Rico Leuthold

unread,
Mar 8, 2011, 3:52:25 AM3/8/11
to flextrine
Hi,

I started working on a bigger project using flextrine and wondered if
anyone has some ideas and considerations on how to use or embed
flextrine in a more complex application.

For example, I found myself using loadAll() for the same repository in
different places of the application, which doesn't look to be so
elegant and right. I tried using mate - since this is the framework I
usually use - but couldn't get it to fit with flextrine in reasonable
way.

What are your experiences with other frameworks and flextrine? How do
you organize your flextrine based application?

Any ideas very welcome.

Thanks
- rico

Dave Keen

unread,
Mar 10, 2011, 5:38:32 AM3/10/11
to flex...@googlegroups.com
I'm currently using Flextrine in a large Robotlegs based project but I expect the principles are broadly the same for any MVC(S) framework.  For what its worth, here's how I think about it:

Service tier:

I view the EntityManager as being the (S)ervice tier.

In fact, in my application I make an EntityService which is simply an adapter onto the methods of the EntityManager.  This means that I can theoretically swap the EntityManager out for a mock to help with testing.  When I need to use some persistence mechanism I inject entityService into my mediators/services/models and do things like:

entityService.persist(myEntity);
entityService.flush();

In fact entityService.flush() does some other things; as well as performing the flush() and returning the AsyncToken, it first attaches result and fault handlers in order to send events/notifications/signals (whatever your framework uses) to the rest of the application.  I use the changeset returned by flush() on success to dispatch a set of entityPersisted, entityUpdated and entityRemoved messages which other modules of my application can respond to.  Likewise on a fault I dispatch a fault message.  See https://github.com/ccapndave/flextrine/wiki/Flush-and-rollback for an example on how to dispatch messages on a successful flush.

Command tier:

It would be totally valid to create PersistCommand, FlushCommand, etc - in my app I haven't done this and haven't yet felt any impact with abstraction or encapsulation, but I guess it could be argued that this is the 'correct' MVC way to do it.

Model tier:


In my head there are two ways to use Flextine.  The first is with configuration option

configuration.entityTimeToLive = -1


This disables garbage collection and basically means that you can treat EntityRepositories as models.  Once data is loaded it goes into the repositories and stays there for you to use, either by directly using the entities collection, or by using the find* helper methods.  This is a nice and easy way to develop, but obviously only useful for applications where you can be sure that you will be only ever loading a limited subset of data.

If entityTimeToLive is set to a positive number, as it is by default, then entities in the repository will be garbage collected when there are no longer a reference to them and can no longer be regarded as models (instead they are kind of caches).  In this case you can use a normal model (however your framework implements them) to store data that you have retrieved from Flextrine.  This means you can control garbage collection, and will eliminate the issue you mentioned of calling loadAll() lots of times.

Take the following pseudo-code as an

public function loadUsers():void {
  em.getRepository(User).loadAll().addResponder(new AsyncResponder(onUserResult, onUserFault));
}

private function onUserResult(e:ResultEvent, token:Object = null):void {
  userModel.setUsers(e.result);
}

View tier:

The mediators in your application can have access to the models and services.  Obviously you don't want the view components themselves to have knowledge of Flextrine, so you provide them with data via their mediator.  The only exception to this is allowing on-demand loading of uninitialized entities and collections.  This can be done for collections using AsyncListView (Flex 4) or just using DataGrid/List (Flex 3), and for entities just using normal data binding.  In AS3 view components the same effect can be acheived using LazyUtil.async.

I hope that helps, and I'd be very keen to hear how other people are using Flextrine with frameworks.  This is by no means the best or correct solution, and Flextrine is purposely vague with regards to MVC so that people can use it however they feel it works best!

Cheers,

Dave
No virus found in this incoming message. Checked by AVG - www.avg.com Version: 9.0.872 / Virus Database: 271.1.1/3489 - Release Date: 03/07/11 21:43:00

Rico Leuthold

unread,
Mar 18, 2011, 9:00:44 AM3/18/11
to flex...@googlegroups.com
Hi Dave,

Thank you for the detailed insight in your use of flextrine together with Robotlegs . I had to chew a bit on your information since I was not familiar with Robotlegs and the terminology and implementation is quite different compared to mate, which I'm normally using. However, it was a good chance to have a first look at another framework.

My approach using mate was to combine your service and Model tier (I'm don't adhere to the MVC pattern if it doesn't seem to fit my needs). 

I created a base class which all the entity managers (manager is a mate manager in this context which can be compared to a Robotlegs Actor) are derived from. In the entity managers I called the parent constructor to pass the specific entity class.

public function EntityManagerBase( cls:Class )
{
_class = cls;
_idField = EntityUtil.getAttributesWithTag( new _class(), MetaTags.ID ); 
}

(Your EntityUtils comes in handy here ;-)

Within the base class I implemented the methods need to load, add and remove entities using flextrine, e.g.,

[Bindable(Event="entitiesChanged")]
public function get entities():EntityCollection
{
if( _entities == null ) { 
_entities = repository.entities;
_sort = new Sort();
_sort.fields = [ new SortField( _idField, true, false )];
_entities.sort = _sort;
}
if( !_entititesLoaded && !_entitiesLoading ) {
loadAll();
}
return _entities;
}

public function loadAll( showBusy:Boolean = true ):void
{
if( !_entitiesLoading && !_entititesLoaded ) {
repository.loadAll().addResponder( new AsyncResponder(onEntitiesLoadResult, onLoadFault) );
busy = showBusy;
} else if ( _entititesLoaded ) {
dispatchEvent( new EntityManagerEvent( EntityManagerEvent.ENTITIES_LOADED) );
}
}

public function addEntity( e:* , save:Boolean = false ):void
{
entityManager.persist( e );
if( save ) {
save();
}
}

public function deleteEntity( e:*, save:Boolean = false ):void
{
entityManager.remove( e );
if( save ) {
save();
}
}

Mate is used to make these managers available to the specific views (using mate ObjectBuilders and PropertyInjectors).

Unfortunately I ran into problems, especially with removing entities. DataProviders have not been updated when they were bound to the repository entities and so on. I never figured out what the problem was.
Furthermore, the EventMap (the core of mate to handle injection and dependency) became so crowded and chaotic, that I decided not to use this approach.

- rico
Reply all
Reply to author
Forward
0 new messages