package org.myeslib.example; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import java.util.Arrays; import java.util.List; import java.util.UUID; import lombok.AllArgsConstructor; import lombok.Data; import lombok.Delegate; import lombok.NonNull; import lombok.Value; import org.myeslib.core.AggregateRoot; import org.myeslib.core.Command; import org.myeslib.core.CommandHandler; import org.myeslib.core.Event; import com.google.common.base.Function; @SuppressWarnings("serial") public class SampleDomain { @Data public static class InventoryItemAggregateRoot implements AggregateRoot { UUID id; String description; Integer available = 0; public void on(InventoryItemCreated event) { this.id = event.id; this.description = event.description; this.available = 0; } public void on(InventoryIncreased event) { this.available = this.available + event.howMany; } public void on(InventoryDecreased event) { this.available = this.available - event.howMany; } public boolean isAvailable(int howMany) { return getAvailable() - howMany >= 0; } } // command handlers @AllArgsConstructor public static class CreateCommandHandler implements CommandHandler<CreateInventoryItem> { @Delegate @NonNull final InventoryItemAggregateRoot aggregateRoot; @NonNull final ItemDescriptionGeneratorService service; public List<? extends Event> handle(CreateInventoryItem command) { checkArgument(getId() == null, "item already exists"); checkNotNull(service); String description = service.generate(command.getId()); InventoryItemCreated event = new InventoryItemCreated(command.getId(), description); return Arrays.asList(event); } } @AllArgsConstructor public static class IncreaseCommandHandler implements CommandHandler<IncreaseInventory> { @Delegate @NonNull final InventoryItemAggregateRoot aggregateRoot; public List<? extends Event> handle(IncreaseInventory command) { checkArgument(getId() != null, "before increasing you must create an item"); checkArgument(getId().equals(command.getId()), "item id does not match"); InventoryIncreased event = new InventoryIncreased(command.getId(), command.getHowMany()); return Arrays.asList(event); } } @AllArgsConstructor public static class DecreaseCommandHandler implements CommandHandler<DecreaseInventory> { @Delegate @NonNull final InventoryItemAggregateRoot aggregateRoot; public List<? extends Event> handle(DecreaseInventory command) { checkArgument(getId() != null, "before decreasing you must create an item"); checkArgument(getId().equals(command.getId()), "item id does not match"); checkArgument(isAvailable(command.howMany), "there are not enough items available"); InventoryDecreased event = new InventoryDecreased(command.getId(), command.getHowMany()); return Arrays.asList(event); } } // commands @Value public static class CreateInventoryItem implements Command { @NonNull UUID commandId; @NonNull UUID id; Long targetVersion = 0L; } @Value public static class IncreaseInventory implements Command { @NonNull UUID commandId; @NonNull UUID id; @NonNull Integer howMany; Long targetVersion; } @Value public static class DecreaseInventory implements Command { @NonNull UUID commandId; @NonNull UUID id; @NonNull Integer howMany; Long targetVersion; } // events @Value public static class InventoryItemCreated implements Event { @NonNull UUID id; @NonNull String description; } @Value public static class InventoryIncreased implements Event { @NonNull UUID id; @NonNull Integer howMany; } @Value public static class InventoryDecreased implements Event { @NonNull UUID id; @NonNull Integer howMany; } // a service just for the sake of the example public static interface ItemDescriptionGeneratorService { String generate(UUID id); } // a factory for the aggregate root public static class InventoryItemInstanceFactory implements Function<Void, InventoryItemAggregateRoot> { @Override public InventoryItemAggregateRoot apply(Void input) { return new InventoryItemAggregateRoot(); } } }