polygene-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Niclas Hedhman <nic...@apache.org>
Subject Re: [jira] [Commented] (ZEST-108) Package renaming to org.apache.zest
Date Sat, 01 Aug 2015 03:52:19 GMT
Roman, (I am taking the liberty of posting this on-list, in case someone
else might find it useful, such as Jeff and James)

Thanks... I have learned one thing when speaking about Qi4j in the past,
and that is to skip a lot of the theory, because there is soooo much of it,
and it simply confuses people. Instead, showing code and what it does,
seems to better blow people away.

The 𝛕 of Zest is the 'control of the method invocation', i.e. Aspects. The
difference with AOP a la AspectJ, is that Zest can do it much more
type-safe, and it is ON by default, and then we have added all kinds of
things around it, like;

   * Composition of fragments into Composites (enhance decoupling)
   * Enforcement of layered applications (enforce cohesion, avoid spaghetti)
   * Constraint validation on method arguments (reduction of defensive
   * Wrap Concerns (around advice) around the method invocation (some
behavior, cross-cutting concerns, etc)
   * 'null' not allowed by default (reduction of defensive programming)
   * Automatic State handling (Property<?> methods don't need an
   * UnitOfWork to define boundaries (persistence, indexing/query, soon
messaging, events)


public interface Order
    void addItem( String inventoryId, int quantity, Currency price );

So, for instance, I want to make sure that the quantity is a positive value;

    void addItem( String inventoryId, *@Positive* int quantity, Currency
price );

where the @Positive annotation is linked to a Constrain implementation,
i.e. totally custom.

@Retention( RetentionPolicy.RUNTIME )
@Constraints( PositiveConstraint.class )
public @interface Positive

public class PositiveConstraint
    implements Constraint<Positive, Number>
    public boolean isValid( Positive range, Number argument ) {
        return argument.doubleValue() >= 0;

The implementation of addItem then doesn't need to check for an illegal

We would need to store the OrderItem somewhere. Let's say that an OrderItem
is another entity, and that we want to keep the Order state private, i.e.
can't be accessed directly from the outside world;

public interface Order
    void addItem( String inventoryId, int quantity, Currency price );

    interface State
        Property<Currency> totalPrice();

        ManyAssociation<OrderItem> items();

And then we can implement the addItem() method, and let's choose to put it
in its own implementation class;

public abstract class AddItemImpl implements Order
    private Order.State state;   // injected reference to the State above.

    public void addItem( String inventoryId, int quantity, Currency price )
        OrderItem item = createItem( inventoryId, quantity, price );
        state.items().add( item );
                state.totalPrice().get().add( price.multiply( quantity ) )

The @This annotation knows how to reach the internal interface State, and
without reflection anything outside the Composite can't get to it. Even
with reflection, it is pretty hard to reach it.

But all of the above is persistence stuff, and it must be executing within
a UnitOfWork. But we have ready-made Concern for handling that for you;

public interface Order
    void addItem( String inventoryId, int quantity, Currency price );

The default @UnitOfWorkPropagation will start a UnitOfWork if none is
already executing, and will complete it if it started it, and discard it if
an exception occurred.

We also have a @UnitOfWorkRetry so the entire method call will be retried
the number of times declared if a ConcurrentEntityModificationException

    @UnitOfWorkRetry( retries=3, initialDelay=5, delayFactor=20 )
    void addItem( String inventoryId, int quantity, Currency price );

I think this covers 10% of the 𝛕 basics, and should wet the appetite quite
a bit, bit let's look at one more advanced example;

public abstract class MyPutMethod implements java.util.Map<K,V>
    public V put( K key, V value )
        // do my magic

During assembly of the application, I can create a new Map composite type,
like this;

    module.transients( Map.class ).withMixins( MyPutMethod.class,
HashMap.class );

This means that the put() method will be taken from MyPutMethod
implementation, but all other methods will be from the HashMap. And more
importantly, when the HashMap is calling its own put() method, it will
actually call MyPutMethod.put().

However, it is not super generic, so one must call the factory methods to
create all these composites.

private TransientBuilderFactory tbf;

Map m = tbd.newTransient( Map.class );

At the end of the day, Zest allows loads of boilerplate to be removed, it
has advanced reflection capabilities, thanks to a strong accessible
composite model that is available to users and by using convention over
configuration, one can reuse much more code than conventional programming.

Hope that helps

On Sat, Aug 1, 2015 at 10:20 AM, Roman Shaposhnik <rvs@apache.org> wrote:

> On Thu, Jul 30, 2015 at 8:28 PM, Niclas Hedhman <niclas@apache.org> wrote:
> >
> > It is probably the 4 largest commits in our history. Idea wasn't capable
> of
> > doing the refactoring of package names (only has 768M heap) in one go.
> But
> > that was the easy part... Changing the 3000 or so references to "qi4j" in
> > class names, variable names, method names and documentation, was the hard
> > part...
> Great story! And I really do think it belongs to your ApacheCON preso.
> Thanks,
> Roman.
> P.S. Speaking of ApacheCON: I'm really looking forward to you presentation
> I've been curious about Zest ever since you proposed its move into ASF, but
> I still can't quite grasp the Tao of it so to speak.

Niclas Hedhman, Software Developer
http://zest.apache.org - New Energy for Java

  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message