As I mentioned to Rickard offline; I don't get it and suggest the full codebase for both cases before I have any bigger opinion, since it is likely I misunderstand what is written below.
-- Niclas On Thu, Aug 18, 2011 at 2:53 PM, Rickard Öberg <[email protected]> wrote: > Hi guys, > > On the DCI mailing list, there has been a lot of discussion about how to > actually implement roles, and what is the "proper way" and what is the > "practical way", and so on. Lately I came up with a variant that seems to > fulfill all the semantical and syntactical criteria, with the only problem > being that the roles are not actually part of the object, but instead are > implemented as wrappers. So, I wanted to run this by you guys and get your > views on this. > > Here's the old way: an Entity would have Data, i.e. Properties and > Associations, and with methods to modify these formulated as imperative > Commands (i.e. "changeFoo"). These can internally be handled by just doing > it (i.e. check if command is ok, and if yes, apply to state), or it can be > done by using event sourcing (i.e. do command check and then call event > method that in turn does the state change). Here's an example from > Streamflow: > @Mixins(Groups.Mixin.class) > public interface Groups > { > Group createGroup( String name ); > > ... methods removed for brevity ... > > interface Data > { > @Aggregated > ManyAssociation<Group> groups(); > } > > interface Events > { > Group createdGroup( @Optional DomainEvent event, String id ); > > void addedGroup( @Optional DomainEvent event, Group group ); > > void removedGroup( @Optional DomainEvent event, Group group ); > } > > class Mixin > implements Groups, Events > { > @This Data data; > > @Service > IdentityGenerator idGen; > > @Structure > Module module; > > public Group createGroup( String name ) > { > Group group = createdGroup(null, idGen.generate(GroupEntity.class)); > group.changeDescription( name ); > addGroup(group); > return group; > } > > public Group createdGroup(@Optional DomainEvent event, String id) > { > return > module.unitOfWorkFactory().currentUnitOfWork().newEntity(Group.class, id); > } > .. the rest of the methods removed for brevity ... > } > } > --- > The above is then extended by an entity interface that needs "Group > management", in my case an OrganizationalUnitEntity. > > So once you have a "Groups" entity, you can invoke the commands directly. > With DCI, the "createGroup" method would only be called from a GroupsContext > where the interaction exist. > > The new way: since GroupsContext is basically the only place from which you > would do this, and it represents a particular usecase, the "createGroup" > method actually belongs in a role in the context, which is only valid *from > within that context*. So, the way to do this is to implement GroupsContext > as a class and then do the Groups commands thing as an inner class that > wraps a Groups object that has Events+Data: > public class GroupsContext > { > // Injection > @Structure > Module module; > > GroupsAdmin groups; > > public GroupsContext() > { > groups = new GroupsAdmin(); // Create wrapper > } > > public GroupsContext rebind(Groups groups) > { > this.groups.bind(groups); // Bind wrapper > return this; > } > > public Iterable<Group> index() > { > return groups.index(); > } > > public Group creategroup( StringValue name ) > { > return groups.createGroup( name.string().get() ); > } > > class GroupsAdmin > extends Role<Groups> > { > Iterable<Group> index() > { > return self.groups(); > } > > Group createGroup(String name) > { > Group group = self.createdGroup(null, > module.serviceFinder().<IdentityGenerator>findService(IdentityGenerator.class).get().generate(GroupEntity.class)); > group.changeDescription(name); > self().addedGroup(null, group); > return group; > } > } > } > --- > The way to use this in client code would be: > new GroupsContext().rebind(groups).createGroup(newName); > > The benefits with this solution are: > * You can create any number of contexts and roles that uses the basic > data/event methods, and don't have to worry with method clashing (major > issue without this solution). > * Role code can access members of the Context that wraps it, e.g. "module". > If there were more bound objects, those could be accessed directly instead > of having them be passed as method arguments to role methods. > * Easier to see the algorithms as they are directly in the context, rather > than having to go one step down into some external mixin. > * Roles are only visible/usable from within the context that defines them. > * Very clear what the dependencies of the context are. > * Invisible from outside that DCI is being used. > > The drawbacks I can see are: > * Since the roles are wrappers you have to think about what to pass around: > wrapper or self() (will almost always be self() since wrapper class is > unknown outside of context) > * Right now cannot use concerns/sideeffects/constraints on inner classes. > Can still use on context interaction if implemented as transient though. > > I'm sure there are more benefits and drawbacks, but this is what I can see > right now. If anyone has other ideas, that would be great. IF this solution > is acceptable, then that changes quite a bit what the typical usage pattern > of Qi4j in a DCI setting would be, so it is kind important. It also makes it > easier to use DCI outside of Qi4j, since there's nothing really in the above > that requires Qi4j, from a pattern point of view. > > WDYT? > > /Rickard > > _______________________________________________ > qi4j-dev mailing list > [email protected] > http://lists.ops4j.org/mailman/listinfo/qi4j-dev > -- Niclas Hedhman, Software Developer http://www.qi4j.org - New Energy for Java I live here; http://tinyurl.com/3xugrbk I work here; http://tinyurl.com/24svnvk I relax here; http://tinyurl.com/2cgsug _______________________________________________ qi4j-dev mailing list [email protected] http://lists.ops4j.org/mailman/listinfo/qi4j-dev

