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 Thu, 28 Apr 2016 23:43:53 GMT
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

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