polygene-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Niclas Hedhman <hedh...@gmail.com>
Subject Re: Aggregates and AggregateRoots
Date Sat, 30 Apr 2016 01:29:47 GMT
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 <hedhman@gmail.com> 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" <kent.soelvsten@gmail.com> 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

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message