Hi,

Thanks for the wrapup Rickard, that was a good read, interesting and exciting. 
I really look forward reading actual DCI 
running code.

In my webapp I use Jersey as REST handler, you use Restlet don't you ?
I'm using TransientComposites as Resources but I did not write the final 
representational code, I'm only outputing 
JSON for now but would like to be able to serve html forms too. One time again, 
I'm looking forward reading the code.

Paul


Le Lundi 15 Février 2010 06:37:24, Rickard Öberg a écrit :
> Hi all,
> 
> Now that Qi4j 1.0 is out the door, I think it's a good time to start
> thinking about what's next. This is a long email, but please bare with
> me, because there's some really crucial points in it I think.
> 
> As you may have noticed the dev list is a bit quiet, but it doesn't mean
> that nothing is happening. On my end, I'm working on the StreamFlow
> project that uses Qi4j, and am coming up with a lot of things that
> eventually will be transferred to the Qi4j project as libraries and
> extensions.
> 
> I'd like to outline some of the highlights which I've found to be
> useful, and which I think would be very useful for Qi4j projects in
> general.
> 
> The first thing is method constraints, which we have already discussed.
> Basically being able to put Constraint annotations on methods instead of
> on parameters, with the semantics being "is this method valid to invoke
> at all or not". This is something which I haven't seen any other
> framework do, and I think it is crucial. One of the key thing it makes
> possible is the ability for outside code to check *before* the method is
> called whether it is possible or not.
> 
> The basic idea would be something like this. In a mixin interface where
> a method depends on the state of the entity, you could add:
> @RequiresStatus(Status.ACTIVE)
> void doSomething(...);
> 
> Where RequiresStatus would have a Constraint implementation like:
> public class RequiresStatusConstraint
>    implements Constraint<RequiresStatus, Status>
> {
>    public boolean isValid(RequiresStatus annotation, Status status>
>    {
>      return status.currentStatus().equals(annotation.value());
>    }
> }
> --
> The "status" parameter comes from simply casting the entity to the
> Status interface, which may not necessarily be the one where the
> doSomething() method is in.
> 
> With this we can both check the constraint on actual calls *and* in
> client code that wants to find out whether the method is valid at all,
> and use this for changing the UI.
> 
> That being said, what I've found in StreamFlow is that you don't really
> care if the domain methods are valid. What is important is whether the
> *usecase* that uses the domain method is valid. It would be the same
> with the Shiro annotations that Paul is working on, since they don't
> really have anything to do with the internal application state, but
> rather with if the usecase that drives a domain model change can be
> invoked considering the context of the usecase, which includes the user
> calling it, and the permissions of the user.
> 
> So how do you implement this? Here's where the DCI (Data, Context,
> Interaction) paradigm comes to the rescue! For me it started by looking
> at "how can I implement a REST API using Qi4j?". And then it struck me
> that URL path's map naturally to the "context" in DCI!
> 
> Let me show you with an example. Let's say we have the following REST URL:
> /users/1234/profile
> 
> The root ("/") does not map to an entity, and neither does "/users". But
> we still want to model it in our application. How to do that? By using
> TransientComposites! I can make a RootContext composite that looks like
> this (this is actual code from StreamFlow):
> @Mixins(RootContext.Mixin.class)
> public interface RootContext
>     extends Context
> {
>     @SubContext
>     UsersContext users();
> 
>     ... other subcontexts ...
> 
>     abstract class Mixin
>        extends ContextMixin
>        implements RootContext
>     {
>        public UsersContext users()
>        {
>           return subContext( UsersContext.class );
>        }
>     }
> }
> ---
> "Context" above extends TransientComposite. ContextMixin has the
> subContext() method which instantiates the given Context subtype. The
> key is that I also inject an InteractionContext object into
> ContextMixin, using @Uses, from the outside which has the following
> methods: public class InteractionContext
> {
>     private Map<Class, Object> roles = new HashMap<Class, Object>( );
>     private List<Object> objects = new ArrayList<Object>( );
> 
>     public void playRoles(Object object, Class... roleClasses)
>     {
>       ... put the object in the roles list and add mappings from
> roleClasses->object in the map
>     }
> 
>     public <T> T role(Class<T> roleClass)
>     {
>        ... return object from mapping using roleClass or find object
>        in list that implements roleClass
>     }
> }
> So before handling a REST call I will populate the InteractionContext
> with the User of the call, for example, so I can then do
> "ContextMixin.context.role(AuthenticatedUser.class)" to retrieve it in
> code.
> 
> If I have the above, then I can have a generic "DCI-REST" handler for
> "/" which reflectively find that I have a @SubContext "users", and then
> present it using HTML if I do a GET:
> <h1>Subcontexts>
> <ul>
> <li><a href="users/" rel="users">users</a></li>
> </ul>
> ---
> If the user puts in "/users" in the browser the handle will instantiate
> the root, and then call users() to get the next level of the context. If
> I only want logged in users to be able to do this I should be able to
> use the @RequiresUser annotation that Paul mentioned *on the users()
> method*. This ensures that any usecases in UsersContext can only be
> accessed by logged in users, so there's no need to check for that in the
> levels below /users.
> 
> What next? In UsersContext I will then put usecases as methods
> (=interactions) that can be used to list users, create users, and select
> them (actual code again):
> @Mixins(UsersContext.Mixin.class)
> public interface UsersContext
>     extends SubContexts<UserContext>, Context
> {
>     UserEntityListDTO users();
> 
>     EntityReference findbyusername( StringDTO name) throws
> ResourceException;
> 
>     // Commands
>     void createuser( NewUserCommand command);
> 
>     abstract class Mixin
>        extends ContextMixin
>        implements UsersContext
>     {
>        public UserEntityListDTO users()
>        {
>           ... list users and return as DTO. REST handler will convert to
> HTML automatically
>        }
> 
>        public EntityReference findbyusername( StringDTO name ) throws
> ResourceException
>        {
>           ... query to find user by name. Since method returns
> EntityReference the REST handler can do redirect to "/users/<id>".
>        }
> 
>        public void createuser( NewUserCommand command )
>        {
>           ...
>        }
> 
>        public UserContext context( String id )
>        {
>           UserEntity user = uowf.currentUnitOfWork().get(
> UserEntity.class, id );
>           context.playRoles( user);
> 
>           return subContext( UserContext.class);
>        }
>     }
> }
> ---
> In this case, instead of using @SubContexts to access static
> subcontexts, I want the next level to be id-based because now I want the
> REST URL to correspond to a particular user. The context(id) method is
> in SubContexts, and using generics to make it statically typed. I also
> look up the actual UserEntity, and put it in the context. The subContext
> call will instantiate the UserContext and forward the InteractionContext
> so that any call from here on that does "context.role(UserEntity.class)"
> or any other mixin type in UserEntity, will return the user. So no code
> below here will have to know about how to translate id's to a looked up
> user.
> 
> Example:
> /users/1234
> translates to:
> <root>.users().context(1234);
> 
> This I can use either for REST purposes, or any other client code (like
> tests) that want to execute interactions on my domain model.
> 
> If I do GET on "/users/1234" the REST handler will hence perform
> <root>.users().context(1234) using reflection, and then show a generated
> HTML form with what methods are available in UserContext.
> 
> And here's where method constraints come in! I can now put constraints
> on the interaction methods in UserContext, rather than in the domain
> model, which declares whether an interaction is available or not.
> 
> Example:
> @Mixins(UserContext.Mixin.class)
> public interface UserContext
>        extends Context
> {
>     @RequiresUserIsOwner
>     void changePassword( ChangePasswordCommand newPassword ) throws
> WrongPasswordException;
>      ...
> }
> When I generate HTML for the above, if the user making the REST call is
> not the same as the user that is being shown, this method will not be
> shown in the HTML.
> 
> The constraint could be implemented as:
> public class RequiresUserIsOwnerConstraint
>    implements Constraint<RequiresUserIsOwner, Context>
> {
>    public boolean isValid(RequiresUserIsOwner a, Context c)
>    {
>      // Get the InteractionContext for this Context
>      InteractionContext context = c.context();
>      // Look up roles from the context
>      // Authenticated user comes from REST handler
>      AuthenticatedUser auth = context.role(AuthenticatedUser.class);
> 
>      // User comes from subContext(UserContext.class) mapping
>      User user = context.role(User.class);
> 
>      return auth.user().equals(user);
>    }
> }
> ---
> So this method constraint on the interaction only uses information *from
> the call* rather than the domain level.
> 
> If I want to be able to disable users in my system, in which case they
> are not allowed to change passwords, I would instead do:
> @Mixins(UserContext.Mixin.class)
> public interface UserContext
>        extends Context
> {
>     @RequiresUserIsOwner @EnabledUser
>     void changePassword( ChangePasswordCommand newPassword ) throws
> WrongPasswordException;
>      ...
> }
> where EnabledUser is implemented as:
> public class EnabledUserConstraint
>    implements Constraint<RequiresUserIsOwner, Context>
> {
>    public boolean isValid(RequiresUserIsOwner a, Context c)
>    {
>      // Get the InteractionContext for this Context
>      InteractionContext context = c.context();
>      // Look up roles from the context
>      // UserEnabled comes from subContext(UserContext.class) mapping
>      UserEnabled user = context.role(UserEnabled.class);
> 
>      return !user.isDisabled();
>    }
> }
> ---
> So with the above, I can both have constraints related to the request
> being made *and* application state. If I only have constraints on domain
> methods this would be impossible, since it doesn't have the extra
> context. Another option is to put the @EnabledUser on the context()
> method that returns the UserContext, so that effectively a disabled user
> "disappears" from the REST API.
> 
> What I've found is that with this I can easily model all the
> interactions in my application, and I can then have a generic REST API
> handler that is generating HTML on GET (including forms for
> interactions!), perform methods on POST/PUT/DELETE, and also support
> HTML for humans browsing the API in a webbrowser, and JSON for
> applications calling the same API. I can perform everything in my
> application using the generated HTML. If I want a nicer UI I create a
> rich client or an GWT client or similar.
> 
> My REST API effectively also becomes self-describing, with no need for
> WSDL or somesuch description language. If the generated HTML also has
> links to the JavaDoc of the context composites a developer can "explore"
> the API without having to have access to contexts and methods in a
> webbrowser. I'm pretty sure there's no other framework on the market
> right now that can do this, especially with the extra conditions that
> methods need to be allowed/disallowed depending on call and application
> state.
> 
> The same context/interaction composites above can then also be used by
> other types of clients, such as BDD tests.
> 
> So far I haven't even mentioned composable contexts, which is an added
> bonus. Here's a quick example. I have many entities which should support
> "descriptions", so they should all have interactions for
> "changeDescription". Instead of duplicating that code in all my
> transient composites I can do:
> public interface SomeContext
>     extends DescribableContext, Context
> {
> ...
> where:
> @Mixins(DescribableContext.Mixin.class)
> public interface DescribableContext
> {
>     public void changedescription( StringDTO stringValue );
> 
>     abstract class Mixin
>        extends ContextMixin
>        implements DescribableContext
>     {
>        public void changedescription( StringDTO stringValue )
>        {
>           Describable describable = context.role( Describable.class );
>           describable.changeDescription( stringValue.string().get() );
>        }
>     }
> }
> ---
> This partial context does not have to know *what* type of entity is
> having its description change, only that it implements Describable and
> is available from the context of the interaction.
> 
> In addition, this solves the problem of how to do aggregate entities.
> With aggregates the rule is that all interactions go through the "root
> aggregate" so that any rules it has are enforced. Normally this would
> require that we put all methods in the actual aggregate entity. This
> approach solves this, since we can spread out the interaction methods in
> contexts, and still be able to enforce aggregate rules. Example: let's
> say that when a user profile is updated the last-modified-timestamp on
> the user is updated. The REST URL would be:
> /users/1234/profile?command=updateprofile
> 
> In ProfileContext, which we would get from a
> UserContext.subContext(ProfileContext) call, we do:
> public void updateprofile(NewProfile profile)
> {
>     UserProfile = context.role(UserProfile.class);
>     userProfile.updateProfile(profile);
>     User user = context.role(User.class);
>     user.updateLastModified(new Date());
> }
> --
> Instead of putting this rule in the updateProfile() method, which would
> tie it to the User class, or even worse, doing something like
> user.updateProfile() which delegates to user.profile.updateProfile(),
> this rule is encapsulated in a place where all the necessary context is
> available (and if this rule should happen for all methods, put it in a
> generic Concern on ProfileContext!). This way aggregate rules are much
> easiler to handle and don't mess up the core domain model.
> 
> Also, the REST handler can contain the logic that if we do
> <root>.users().context(1234) it can then do a redirect to a server which
> should handle user "1234" (if we have sharding in place).
> 
> To sum up: by using transientcomposites along with a clever usage of the
> InteractionContext we can support DCI fully, including subcontexts and
> composable contexts (the DescribableContext "trick" above). This also
> *happens* to be perfect for REST API's, and also for use in tests (i.e.
> you do the tests on the context/interaction rather than the raw domain
> model).
> 
> To me, this would be an important next step for Qi4j, and would make it
> support DCI, REST, aggregates and BDD in a simple and logical way.
> 
> WDYT?
> 
> /Rickard
> 
> _______________________________________________
> qi4j-dev mailing list
> [email protected]
> http://lists.ops4j.org/mailman/listinfo/qi4j-dev



_______________________________________________
qi4j-dev mailing list
[email protected]
http://lists.ops4j.org/mailman/listinfo/qi4j-dev

Reply via email to