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:
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.
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.