After thinking about this for a while, my conclusion is that on one hand I like it a lot, but it would also mean that we need Aggregation(), ManyAggregation(), NamedAggregation() in parallel of the existing Associations, for something that is really the same-ish thing.
It isn't out of the question, but to me it feels like a lot of user-facing surface area for relatively little purpose. Could this be modified a bit?? EntityBuilder<OrderItem> builder = uowf.newAggregation( myOrder.items() ); I think this looks very neat, and should solve the "what is its association" in the UoW part quite easily. WDYAT? Niclas On Fri, Apr 29, 2016 at 7:43 AM, Niclas Hedhman <[email protected]> wrote: > > I like this idea. It is very Zest-like, in that it is "think outside the > box"... (There is no spoon) > > I want to ponder over a little bit. > > Cheers > Niclas > On Apr 29, 2016 05:28, "Kent Sølvsten" <[email protected]> wrote: > > Another topic is how to manage the OrderItems inside the Order. > > I have an idea .... > > You discussed a special UOWFactory as part of the aggregate composite - > what should that UOW be used for? I guess it is just for adding/removing > OrderItems. > Could that be part of the relation? > > what if , instead of > > @Aggregated > ManyAssociation<OrderItem> items(); > > > we have > > ManyAggregation<OrderItem> items(); > > An aggregation is a relation, similar to the association. But we cannot > add items to it, instead it helps us building new parts. > > EntityBuilder<OrderItem> template = items().newEntityBuilder(); > template.quantity().set(1); > template.newInstance(); // will automatically be part of the collection > > and the later use the aggregation to remove an item. > > That is OrderItems are not created independently and then added, instead > they are created directly on the aggregation! > > /Kent > > > > > > Den 28-04-2016 kl. 17:01 skrev Niclas Hedhman: > > Gang, > > > > since we are on the topic of persistence, I have some thoughts that I > would > > like to share. > > > > I think code is the best way to show, and as usual we start with a > usecase. > > > > So, let's say I want to create the classic Order example with OrderItem > > entities (orders can be massively big doh!!! (should really just use > values > > as items)). Any way, so I have the OrderItem > > > > public interface OrderItem extends EntityComposite > > { > > Property<Integer> quantity(); > > Association<Product> product(); > > Property<BigDecimal> pricePerItem(); > > Property<BigDecimal> discount(); > > } > > > > The we have the Order itself... > > > > public interface Order extends EntityComposite > > { > > Customer customer(); > > BigDecimal totalSum(); > > void addItem( Product p, int quantity, BigDecimal price, BigDecimal > > discount ); > > void removeItem( Product p, int quantity ); > > Iterable<OrderItem> items(); > > > > interface State > > { > > Association<Customer> customer(); > > > > Property<ShippingAddress> shippingAddress(); > > > > @Aggregated > > ManyAssociation<OrderItem> items(); > > > > Property<Boolean> onCredit(); > > } > > > > } > > > > Let's say that we just want to make sure an order doesn't exceed the > credit > > limit of the customer. > > > > For sake of simplicity in this discussion, the current outstanding credit > > is stored in the Customer itself and a simple exposed method for the > > validation. > > > > public interface Customer > > { > > : > > boolean hasCreditFor( BigDecimal additional ); > > : > > } > > > > We already have a validation mechanism called Constraints, but they work > on > > input arguments to methods. We need something similar, but work on the > > entire Composite, > > > > public class CreditLimitConstraint > > implements AggregateConstraint<Order> > > { > > public boolean isValid( Order order ) > > { > > if( ! order.onCredit().get() ) > > return true; > > return order.customer().hasCreditFor( order.totalSum() ); > > } > > } > > > > And we could annotate with a Constraint-like annotation, > > > > @AggregateConstraints( CreditLimitConstraint.class ) > > public interface Order extends EntityComposite > > {} > > > > But this doesn't solve the problem. We could have code that queries for > the > > OrderItem instances and manipulates them directly. That is against the > > principle of Aggregates. Or we could hand over a reference of an > OrderItem > > to another composite which use it to direct access to the aggregated > > entity. Also not cool. > > > > So, what we need is that EntityReference of an aggregated Entity to be > > invalid if it is not accessed from the AggregateRoot instance. > > > > How can we do that, in relatively simple terms? > > > > Well, one option could be to create a new composite meta type; > > > > public interface Aggregate extends EntityComposite, UnitOfWorkFactory{} > > > > And its implementation of UnitOfWorkFactory has two purposes; > > 1. Only work for types that exists as @Aggregated Associations within > the > > same subtype. > > 2. Manage the EntityReferences to be Aggregate "relative". > > > > The second of those is a bit tricky, but effectively needs to provide a > UoW > > implementation that leverage the EntityReference of the owning Aggregate. > > Doable, but tricky. > > > > > > But hold on a second; If the Aggregate is a UnitOfWorkFactory and > possibly > > handles its own UnitOfWork, how does this fit with DDD on one hand and > with > > the current UoW system on the other hand?? > > > > Well, with DDD it is a perfect match. The Aggregate is a transactional > > boundary. So it seems to suggest a good fit. Cool. > > But, does DDD really tackle this, since you can't get the > UnitOfWorkFactory > > of the Aggregate without first having a UnitOfWork to retrieve it with. > > This seems to suggest that DDD doesn't have it correct, or has made a > > simplification that is somewhat unfortunate. > > > > Could it be that there is actually a difference between the Aggregate > being > > a non-Entity composite and the AggregateRoot that is an entity and part > of > > the Aggregate?? > > > > Perhaps... > > > > public interface Order extends EntityComposite {} // back to normal > entity > > > > @AggregateConstraint( CreditLimitConstraint.class ) > > public interface OrderAggregate extends Aggregate<Order> {} > > > > meaning > > > > public interface Aggregate<T extends EntityComposite> extends > > UnitOfWorkFactory, Composite > > { > > T root(); > > } > > > > (I have place EntityComposite supertypes everywhere, but that is for > > clarity. Since we don't require that anymore, they shouldn't be there) > > > > So, then how would this be used?? > > > > Aggregate<Order> aggregate = module.newAggregate( Order.class, identity > ); > > > > the newAggregate method, would do > > a. create Aggregate composite, > > b. create a new unit of work > > c. read the entity from the store and populate root() > > d. if entity doesn't exists, create a entity builder internally and > use > > when populating state, > > e. keep the UoW open, > > > > Since it should handle both existing and new aggregates uniformly, I > think > > a > > > > boolean isCreating(); // or similar > > > > method should also exist on the Aggregate type. > > > > > > To me, this feels a lot better. The DDD "rules" and "intents" are still > in > > effect and can be enforced, and we can manage to implement it in Zest, if > > we choose to do so. > > > > I am really keen on hearing thoughts on this topic. > > > > Cheers > > -- Niclas Hedhman, Software Developer http://zest.apache.org - New Energy for Java
