Zest™
Introduction
Tutorials
Javadoc
Samples
Core
Libraries
Extensions
Tools
Glossary 

Zest™ in 10 minutes

Tip

Theses tutorials are based on actual code found in the tutorials/ directory of the Zest™ SDK sources. You should start your favorite editor and find the code related to this tutorial, run it and play with it.

If you want to reproduce what’s explained in this tutorial, remember to depend on the Core Runtime artifact that depends on Core API, Core SPI, Core Bootstrap and Core Functional & I/O APIs:

Table 2. Artifact

Group IDArtifact IDVersion

org.qi4j.core

org.qi4j.core.runtime

2.1


Moreover, you’ll need an EntityStore for persistence and an Indexing engine for querying. Choose among the available implementations listed in the Extensions section.

See the Depend on Zest™ in your build tutorial for details.

Composition is done with Java interfaces and Annotations. Example;

@Concerns( { PurchaseLimitConcern.class, InventoryConcern.class } )
public interface OrderEntity
    extends Order, Confirmable,
            HasSequenceNumber, HasCustomer, HasLineItems,
            EntityComposite
{
}

This Composite is potentially complete. The Composite interface has a Mixin declared which is always present, the PropertyMixin, which will handle all properties we use. The two Concerns are interceptors that are placed on the methods that they declare, for instance;

public class InventoryConcern extends ConcernOf<Order>
    implements Order
{
    @Service
    private InventoryService inventory;

    @Override
    public void addLineItem( LineItem item )
    {
        String productCode = item.productCode().get();
        int quantity = item.quantity().get();
        inventory.remove( productCode, quantity );
        next.addLineItem( item );
    }

    @Override
    public void removeLineItem( LineItem item )
    {
        String productCode = item.productCode().get();
        int quantity = item.quantity().get();
        inventory.add( productCode, quantity );
        next.removeLineItem( item );
    }
}

Extending the ConcernOf is a convenience mechanism, instead of an explicit @ConcernFor annotation on a private field, which can be used in rare occasions when you are not able to extend. This base class defines the next field, which is set up by the Zest™ runtime and points to the next fragment in the call stack.

We can also see that the InventoryService is provided to the concern, which is done with dependency injection. Zest™ also supports dependency injection via constructors and methods.

The above example is obviously doing persistence, and we have no code handling this. But Zest™ supports persistence directly in its Core, and it is taken care of by Zest™, since it is declared as an EntityComposite. Nothing else is needed, provided that the Zest™ Runtime has been setup with one or more persisted EntityStores. But we have a naming convention that EntityComposites have "Entity" as the suffix in its name.

There are other built-in Composite subtypes as well, such as ValueComposite and ServiceComposite. This distinction helps both to communicate intent as well as having more precisely defined functionality.

Now, let’s say that we want to send a mail to sales@mycompany.com when the order is confirmed. This is a SideEffect, and will execute after the Constraints, Concerns and Mixins. We add the SideEffect to the OrderEntity;

@SideEffects( MailNotifySideEffect.class )
@Concerns( { PurchaseLimitConcern.class, InventoryConcern.class } )
public interface OrderEntity
    extends Order, Confirmable,
            HasSequenceNumber, HasCustomer, HasLineItems,
            EntityComposite
{

The SideEffect implementation is fairly simple.

public abstract class MailNotifySideEffect extends SideEffectOf<Confirmable>
    implements Confirmable
{
    @Service
    private MailService mailer;

    @This
    private HasLineItems hasItems;

    @This
    private HasCustomer hasCustomer;

    @Override
    public void confirm()
    {
        StringBuilder builder = new StringBuilder();
        builder.append( "An Order has been made.\n\n\n" );
        builder.append( "Customer:" );
        builder.append( hasCustomer.name().get() );
        builder.append( "\n\nItems ordered:\n" );
        for( LineItem item : hasItems.lineItems().get() )
        {
            builder.append( item.name().get() );
            builder.append( " : " );
            builder.append( item.quantity().get() );
            builder.append( "\n" );
        }
        mailer.send( "sales@mycompany.com", builder.toString() );
    }
}

The MailService is dependency injected, as we have seen before.

@This is telling Zest™ that the SideEffect needs a reference to the Composite instance that it belongs to.

By asking for both the HasCustomer and the HasLineItems types, we get type-safety and don’t need to bother with casts. In fact, Zest™ will ensure that you can’t even cast the hasCustomer instance to the HasLineItems type.

By not referencing the aggregated interface OrderEntity, we reduce the coupling of this SideEffect and it can be used in any other Composite where the HasCustomer and HasLineItems combination is used, for instance in an InvoiceEntity.

So, build the report, send it via the MailService.

Conclusion

In this short introduction, we have covered the essence of Zest™. We have looked at what is a Composite, seen some of the Fragments in action, and how simple it is to turn a Composite into a persisted Composite, known as an EntityComposite.

Next step, Zest™ in 30 minutes.