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

Reply via email to