Interesting.  Except for the bit with having to implement interfaces + cover 
methods; you've now "polluted" your code with mixin details.  All-in-all, a 
nice idea, though.  I wonder if we could incorporate mixins (custom or 
otherwise?) directly into the modeler + class generation so that the interface 
+ cover methods are auto-generated into the DO superclass? Not sure about this, 
just "thinking out loud".  Maybe if the DO mixins could, themselves, be 
annotated, like:

@CayenneMixin(interfaceClass=Referenceable.class, 
template="SomeFile.txt")//both interfaceClass and template would be optional... 
template would contain some blurb that the superclass generator "mixes in" to 
superclass templates.
public @interface ReferenceableMixin {
}

Now you potentially have a way to scan for mixins/gain additional information 
about them...  anyway, just considering ways to write less duplicate code. :)

Robert

On Nov 1, 2010, at 11/16:27 PM , Andrus Adamchik wrote:

> Been thinking about work-related design issues, and came up with an idea of 
> DataObject "mixins". So what are the problems:
> 
> 1. Often you'd like to associate a certain set of common properties and/or 
> behavior with a group of persistent objects that are unrelated and not a part 
> of the same inheritance hierarchy. Here is an abstracted real-life example:
> 
> * entities A, B, C are "referenceable" (they have a public UUID)
> * entities A, C, D are "auditable" (when a user changes their data in some 
> way, a system must record the change history)
> * entities A, C and E are "access-controlled" (a certain permission level is 
> required for a user to view or edit them).
> 
> 2. In a layered architecture, DataObject itself may not be the right place to 
> implement complex behavior. All non-data methods should ideally be pluggable 
> (normally meaning must be IoC-driven), but still attached to a specific 
> DataObject.
> 
> In other words there's no multiple inheritance in Java, and anyways we want 
> to push most of the logic to the higher app layers. So... I am experimenting 
> with something I called "mixins", based on DataObject dynamic nature + 
> listener capabilities. I checked in to github a simple mixin extension for 
> Cayenne:
> 
> http://github.com/andrus/cayenne-mixin/tree/master/other/cayenne-mixin/
> 
> and a proto-CMS using it:
> 
> http://github.com/andrus/cayenne-mixin/tree/master/oc/
> 
> I may actually keep developing the CMS piece for my own needs, but here a 
> very basic version of it is used for a mixin demo. A mixin in the simplest 
> form is just a custom annotation placed on a DataObject:
> 
>  @ReferenceableMixin
>  @AuditableMixin
>  public class Article extends _Article { }
> 
> It is up to the application to attach lifecycle handlers for a given type of 
> mixin. MixinHandlerManager class from cayenne-mixin module provides API to 
> bind such handlers:
> 
>  MixinHandlerManager handlerManager = new MixinHandlerManager(entityResolver);
>  handlerManager.addMixinHandler(handler);
> 
> A handler implements MixinHandler interface and does whatever is needed to 
> the object during its lifecycle. Below is a mixin handler that calculates and 
> injects a UUID property in a referenceable object:
> 
>  class ReferenceableMixinHandler implements MixinHandler<ReferenceableMixin> {
> 
>       @Override
>       public Class<ReferenceableMixin> getMixinType() {
>               return ReferenceableMixin.class;
>       }
> 
>       @Override
>       public void setupListeners(LifecycleCallbackRegistry registry,
>                               Class<? extends DataObject> entityType) {
> 
>               registry.addListener(LifecycleEvent.POST_PERSIST, entityType, 
> this,
>                               "initUuidCallback");
>               registry.addListener(LifecycleEvent.POST_LOAD, entityType, this,
>                               "initUuidCallback");
>       }
> 
>       void initUuidCallback(DataObject object) {
>               int id = DataObjectUtils.intPKForObject(object);
>               String uuid = object.getObjectId().getEntityName() + ":" + id;
>               object.writePropertyDirectly(Referenceable.UUID_PROPERTY, uuid);
>       }
>  }
> 
> Of course any DataObject can store any transient property, so we are taking 
> advantage of that. Similarly AuditableMixinHandler is used to create 
> ContentVersion records when an object changes. I am still exploring various 
> uses of mixins and ways to extract common handler patterns in cayenne-mixin. 
> A few early observations:
> 
> * Mixins provide a better way to organize and understand listeners. From 
> experience, ad-hoc mapping of listeners quickly results in a mess - each 
> listener maps to more than 1 entity and more than 1 type of events. Very hard 
> to remember and manage that stuff. I'd rather not think about event types at 
> all, and just think that e.g. a listener manages 'uuid' property for a set of 
> referenceable objects.
> 
> * Mixins may be a better way for inheritance mapping. I am still to explore 
> this idea, but I suspect in many cases mixins may be more scaleable than e.g. 
> vertical inheritance. Besides of course they allow for many "superclasses" at 
> once.
> 
> * Mixins may provide a facility for custom relationship faulting. E.g. an 
> injected property can be a lazy collection backed by a custom query (maybe 
> try overriding standard Cayenne relationships this way?)
> 
> * There are some limitations, most obviously all mixin properties have to be 
> accessed via DataObject.readProperty(..) which may not fly well with 
> scripting (e.g. "article.uuid"). This can be solved by an optional mixin 
> interface and manual cover methods. Not ideal, but works:
> 
>  public class Article extends _Article implements Referenceable {
> 
>       @Override
>       public String getUuid() {
>               return (String) readProperty(Referenceable.UUID_PROPERTY);
>       }
>  }
> 
> 
> Anyways, this is something to explore. Mixins solve a whole class of design 
> problems for me, and I am sure we can find other uses.
> 
> Cheers,
> Andrus
> 
> 
> 

Reply via email to