// Path: cqrs-lib/src/main/java/se/citerus/cqrs/bookstore/domain/AggregateRoot.java
// public class AggregateRoot<T extends GenericId> {
// 
//   private final List<DomainEvent> uncommittedEvents = new ArrayList<>();
// 
//   private T id;
//   private int version = 0;
//   private long timestamp = 0;
// 
//   public List<DomainEvent> getUncommittedEvents() {
//     return uncommittedEvents;
//   }
// 
//   public void loadFromHistory(List<DomainEvent> history) {
//     for (DomainEvent event : history) {
//       applyChange(event, false);
//     }
//   }
// 
//   protected int nextVersion() {
//     return version + 1;
//   }
// 
//   protected long now() {
//     return System.currentTimeMillis();
//   }
// 
//   public T id() {
//     return id;
//   }
// 
//   public int version() {
//     return version;
//   }
// 
//   public long timestamp() {
//     return timestamp;
//   }
// 
//   protected void applyChange(DomainEvent event) {
//     applyChange(event, true);
//   }
// 
//   private void applyChange(DomainEvent event, boolean isNew) {
//     updateMetadata(event);
//     invokeHandlerMethod(event);
//     if (isNew) uncommittedEvents.add(event);
//   }
// 
//   private void updateMetadata(DomainEvent event) {
//     this.id = (T) event.aggregateId;
//     this.version = event.version;
//     this.timestamp = event.timestamp;
//   }
// 
//   private void invokeHandlerMethod(DomainEvent event) {
//     Method handlerMethod = getHandlerMethod(event);
//     if (handlerMethod != null) {
//       handlerMethod.setAccessible(true);
//       try {
//         handlerMethod.invoke(this, event);
//       } catch (Exception e) {
//         throw new RuntimeException("Unable to call event handler method for " + event.getClass().getName(), e);
//       }
//     }
//   }
// 
//   private Method getHandlerMethod(DomainEvent event) {
//     try {
//       return getClass().getDeclaredMethod("handleEvent", event.getClass());
//     } catch (NoSuchMethodException e) {
//       return null;
//     }
//   }
// 
//   public boolean hasUncommittedEvents() {
//     return !uncommittedEvents.isEmpty();
//   }
// 
// }
// 
// Path: order-context-parent/eventbus-contract/src/main/java/se/citerus/cqrs/bookstore/ordercontext/order/ProductId.java
// public class ProductId extends GenericId {
// 
//   public ProductId(@JsonProperty("id") String id) {
//     super(id);
//   }
// 
//   public static ProductId randomId() {
//     return new ProductId(UUID.randomUUID().toString());
//   }
// 
// }
// 
// Path: order-context-parent/eventbus-contract/src/main/java/se/citerus/cqrs/bookstore/ordercontext/publishercontract/PublisherContractId.java
// public class PublisherContractId extends GenericId {
// 
//   public PublisherContractId(@JsonProperty("id") String id) {
//     super(id);
//   }
// 
//   public static PublisherContractId randomId() {
//     return new PublisherContractId(UUID.randomUUID().toString());
//   }
// 
// }
// 
// Path: order-context-parent/eventbus-contract/src/main/java/se/citerus/cqrs/bookstore/ordercontext/publishercontract/event/PublisherContractRegisteredEvent.java
// public class PublisherContractRegisteredEvent extends DomainEvent<PublisherContractId> {
// 
//   public final String publisherName;
//   public final double feePercentage;
//   public final long limit;
// 
//   public PublisherContractRegisteredEvent(@JsonProperty("aggregateId") PublisherContractId aggregateId,
//                                           @JsonProperty("version") int version,
//                                           @JsonProperty("timestamp") long timestamp,
//                                           @JsonProperty("publisherName") String publisherName,
//                                           @JsonProperty("feePercentage") double feePercentage,
//                                           @JsonProperty("limit") long limit) {
//     super(aggregateId, version, timestamp);
//     this.publisherName = publisherName;
//     this.feePercentage = feePercentage;
//     this.limit = limit;
//   }
// 
// }
// 
// Path: order-context-parent/eventbus-contract/src/main/java/se/citerus/cqrs/bookstore/ordercontext/publishercontract/event/PurchaseRegisteredEvent.java
// public class PurchaseRegisteredEvent extends DomainEvent<PublisherContractId> {
// 
//   public final ProductId productId;
//   public final long purchaseAmount;
//   public final long feeAmount;
//   public final long accumulatedFee;
// 
//   public PurchaseRegisteredEvent(@JsonProperty("aggregateId") PublisherContractId aggregateId,
//                                  @JsonProperty("version") int version,
//                                  @JsonProperty("timestamp") long timestamp,
//                                  @JsonProperty("productId") ProductId productId,
//                                  @JsonProperty("purchaseAmount") long purchaseAmount,
//                                  @JsonProperty("feeAmount") long feeAmount,
//                                  @JsonProperty("accumulatedFee") long accumulatedFee) {
//     super(aggregateId, version, timestamp);
//     this.productId = productId;
//     this.purchaseAmount = purchaseAmount;
//     this.feeAmount = feeAmount;
//     this.accumulatedFee = accumulatedFee;
//   }
// 
// }


import se.citerus.cqrs.bookstore.domain.AggregateRoot;
import se.citerus.cqrs.bookstore.ordercontext.order.ProductId;
import se.citerus.cqrs.bookstore.ordercontext.publishercontract.PublisherContractId;
import se.citerus.cqrs.bookstore.ordercontext.publishercontract.event.PublisherContractRegisteredEvent;
import se.citerus.cqrs.bookstore.ordercontext.publishercontract.event.PurchaseRegisteredEvent;
import static com.google.common.base.Preconditions.checkState;
package se.citerus.cqrs.bookstore.ordercontext.publishercontract.domain;



public class PublisherContract extends AggregateRoot<PublisherContractId> {

  private double feePercentage;
  private long limit;
  private long accumulatedFee;

  public void register(PublisherContractId publisherContractId, String name, double feePercentage, long limit) {
    assertHasNotBeenRegistered();
    applyChange(new PublisherContractRegisteredEvent(publisherContractId, nextVersion(), now(), name, feePercentage, limit));
  }

// Path: cqrs-lib/src/main/java/se/citerus/cqrs/bookstore/domain/AggregateRoot.java
// public class AggregateRoot<T extends GenericId> {
// 
//   private final List<DomainEvent> uncommittedEvents = new ArrayList<>();
// 
//   private T id;
//   private int version = 0;
//   private long timestamp = 0;
// 
//   public List<DomainEvent> getUncommittedEvents() {
//     return uncommittedEvents;
//   }
// 
//   public void loadFromHistory(List<DomainEvent> history) {
//     for (DomainEvent event : history) {
//       applyChange(event, false);
//     }
//   }
// 
//   protected int nextVersion() {
//     return version + 1;
//   }
// 
//   protected long now() {
//     return System.currentTimeMillis();
//   }
// 
//   public T id() {
//     return id;
//   }
// 
//   public int version() {
//     return version;
//   }
// 
//   public long timestamp() {
//     return timestamp;
//   }
// 
//   protected void applyChange(DomainEvent event) {
//     applyChange(event, true);
//   }
// 
//   private void applyChange(DomainEvent event, boolean isNew) {
//     updateMetadata(event);
//     invokeHandlerMethod(event);
//     if (isNew) uncommittedEvents.add(event);
//   }
// 
//   private void updateMetadata(DomainEvent event) {
//     this.id = (T) event.aggregateId;
//     this.version = event.version;
//     this.timestamp = event.timestamp;
//   }
// 
//   private void invokeHandlerMethod(DomainEvent event) {
//     Method handlerMethod = getHandlerMethod(event);
//     if (handlerMethod != null) {
//       handlerMethod.setAccessible(true);
//       try {
//         handlerMethod.invoke(this, event);
//       } catch (Exception e) {
//         throw new RuntimeException("Unable to call event handler method for " + event.getClass().getName(), e);
//       }
//     }
//   }
// 
//   private Method getHandlerMethod(DomainEvent event) {
//     try {
//       return getClass().getDeclaredMethod("handleEvent", event.getClass());
//     } catch (NoSuchMethodException e) {
//       return null;
//     }
//   }
// 
//   public boolean hasUncommittedEvents() {
//     return !uncommittedEvents.isEmpty();
//   }
// 
// }
// 
// Path: order-context-parent/eventbus-contract/src/main/java/se/citerus/cqrs/bookstore/ordercontext/order/ProductId.java
// public class ProductId extends GenericId {
// 
//   public ProductId(@JsonProperty("id") String id) {
//     super(id);
//   }
// 
//   public static ProductId randomId() {
//     return new ProductId(UUID.randomUUID().toString());
//   }
// 
// }
// 
// Path: order-context-parent/eventbus-contract/src/main/java/se/citerus/cqrs/bookstore/ordercontext/publishercontract/PublisherContractId.java
// public class PublisherContractId extends GenericId {
// 
//   public PublisherContractId(@JsonProperty("id") String id) {
//     super(id);
//   }
// 
//   public static PublisherContractId randomId() {
//     return new PublisherContractId(UUID.randomUUID().toString());
//   }
// 
// }
// 
// Path: order-context-parent/eventbus-contract/src/main/java/se/citerus/cqrs/bookstore/ordercontext/publishercontract/event/PublisherContractRegisteredEvent.java
// public class PublisherContractRegisteredEvent extends DomainEvent<PublisherContractId> {
// 
//   public final String publisherName;
//   public final double feePercentage;
//   public final long limit;
// 
//   public PublisherContractRegisteredEvent(@JsonProperty("aggregateId") PublisherContractId aggregateId,
//                                           @JsonProperty("version") int version,
//                                           @JsonProperty("timestamp") long timestamp,
//                                           @JsonProperty("publisherName") String publisherName,
//                                           @JsonProperty("feePercentage") double feePercentage,
//                                           @JsonProperty("limit") long limit) {
//     super(aggregateId, version, timestamp);
//     this.publisherName = publisherName;
//     this.feePercentage = feePercentage;
//     this.limit = limit;
//   }
// 
// }
// 
// Path: order-context-parent/eventbus-contract/src/main/java/se/citerus/cqrs/bookstore/ordercontext/publishercontract/event/PurchaseRegisteredEvent.java
// public class PurchaseRegisteredEvent extends DomainEvent<PublisherContractId> {
// 
//   public final ProductId productId;
//   public final long purchaseAmount;
//   public final long feeAmount;
//   public final long accumulatedFee;
// 
//   public PurchaseRegisteredEvent(@JsonProperty("aggregateId") PublisherContractId aggregateId,
//                                  @JsonProperty("version") int version,
//                                  @JsonProperty("timestamp") long timestamp,
//                                  @JsonProperty("productId") ProductId productId,
//                                  @JsonProperty("purchaseAmount") long purchaseAmount,
//                                  @JsonProperty("feeAmount") long feeAmount,
//                                  @JsonProperty("accumulatedFee") long accumulatedFee) {
//     super(aggregateId, version, timestamp);
//     this.productId = productId;
//     this.purchaseAmount = purchaseAmount;
//     this.feeAmount = feeAmount;
//     this.accumulatedFee = accumulatedFee;
//   }
// 
// }


// Path: order-context-parent/publisher-contract-command/src/main/java/se/citerus/cqrs/bookstore/ordercontext/publishercontract/domain/PublisherContract.java
import se.citerus.cqrs.bookstore.domain.AggregateRoot;
import se.citerus.cqrs.bookstore.ordercontext.order.ProductId;
import se.citerus.cqrs.bookstore.ordercontext.publishercontract.PublisherContractId;
import se.citerus.cqrs.bookstore.ordercontext.publishercontract.event.PublisherContractRegisteredEvent;
import se.citerus.cqrs.bookstore.ordercontext.publishercontract.event.PurchaseRegisteredEvent;
import static com.google.common.base.Preconditions.checkState;

package se.citerus.cqrs.bookstore.ordercontext.publishercontract.domain;



public class PublisherContract extends AggregateRoot<PublisherContractId> {

  private double feePercentage;
  private long limit;
  private long accumulatedFee;

  public void register(PublisherContractId publisherContractId, String name, double feePercentage, long limit) {
    assertHasNotBeenRegistered();
    applyChange(new PublisherContractRegisteredEvent(publisherContractId, nextVersion(), now(), name, feePercentage, limit));
  }

  public void registerPurchase(ProductId productId, long unitPrice, int quantity) {